- Published on
Benefiting from synchronous API’s in iOS apps
Waiting for some operation to complete is a common task in every iOS app, making a network request or a query to CoreData
are some examples. A way to implement this asynchrony
is with the usage of completion handlers (callbacks).
func load(completion: @escaping (Result) -> Void)
In the domain layer I have a protocol that defines how an image (Data) should be loaded, again, a completion handler is used to complete the operation after some work is done:
But, waiting the time for a task to complete is a threading detail and it should not leak implementations into the domain layer. It may be better to strive for a simpler, synchronous API.
This way, the threading detail is removed from the domain layer and the callback is eliminated (reducing complexity). I’ve also removed the FeedImageLoaderTask
because i’m using Combine
to handle the asynchronous events (cancelling comes for free).
Main Thread block
Making this operation synchronous means that it is now executed on the main queue
, which can block the app’s UI from responding:
To correct this behaviour we can use receive(on:), a publisher that “affects upstream messages”, to make the operations run in a background execution context. In this case I’m using a separate background queue. The components are thread safe so it can be concurrent
as well ✅.
Type erasure
Just a detail, eraseToAnyScheduler()
here is used so I can inject a immediate serial queue when running the acceptance tests, this way the test cases don’t have to wait for the background tasks to complete (they’ll finish faster 🏎). You can check its implementation here.