VIPER. This way, all the logic is contained in a synchronous interactor which is easy to unit test. And the presenter will only be responsible of calling the interactor and the asynchronicity management.
Developing all our network calls this way has produced some desirable side effects which are listed below.
If the feature in the iOS app is ready before it is available in the backend, we can still show it to our product manager or client for validation purposes. Moreover the files that we used to stub the server’s responses can be used as a reference for the backend development. And we can update them after the backend is ready to check that everything is working as expected.
Having this stubbed API enables us to have better UI tests by making us able to test all the edge cases if we want to. It also makes it easier to have up to date app screenshots if we choose to automate them using fastlane/snaphot.
The synchronous services make it easier to have integration tests that will call the app implementation of the API protocol. This will help us check that we are calling the backend and that it is responding as expected.
If it is not possible to test all the endpoints, we can imagine having a critical scenario running regularly on an integration server and checking that there is no major issue. For example:
Chris Lattner recently published a manifesto on github. His goal was to start a conversation about what features should the Swift language offer to handle concurrency. He proposed an approach consisting of introducing the async and await keywords. An asynchronous function will have the async keyword in its declaration and when we call it we add the await keyword to wait for the response.
The publication of this manifesto reassured us about the direction we took when we were trying to improve our network calls and made us look forward to a better concurrency handling in the Swift language.