Mocking a remote API in iOS based on a protocol-oriented design, using Swift
A really simple technique in Swift, which takes advantage of protocols, will allow you to mock your app’s external API with almost no effort at all.
In your current assignment, you’re certainly working on a project that implies communicating with an external server that feeds data into your iOS app. The dependency on this external resource creates an opportunity to implement a mocked counterpart as we can actually spend the majority of our development time targeting the mock API instead of the real API.
Nowadays, I tend to create an internal mocked API object, living inside my app, so that I can have full control over this dependency while developing the app. This gives me some obvious advantages:
- I can keep working if the API is unavailable as my mock API is a guaranteed 100%-uptime system
- I no longer depend on the network layer’s speed. Actually, I design the mock API so that I can fake an asynchronous request using a time delay but, unless I want to know something like how does a UIActivityIndicatorView behave under certain conditions, I can simply get rid of the delay and speed up my work. On the other hand, I can easily fake longer delays if I need to see how the app works under low-speed internet connections, without the need to use any hack to throttle down my internet connection
- I can manipulate the data as I please with effortless ease. Even if I have a development environment (i.e., running the server on my own machine) or access to a remote staging server, if I just need a subtle change in the backend to test how the iOS frontend reacts to that, I need to either go to some web interface to change that data or, if this doesn’t even exist, I need to go directly to the database to make the changes (ultimately, I could even use a proxy like Charles to edit the response before it gets to the app). Anyway, all of these approaches involve several steps that may take some time to achieve, even if I just want to change a single field in the json I’m getting from the API. Now, what if I had a mock API object that responds with data present in a local json file? This makes data manipulation as simple as editing a single file, accessible from XCode
- I can step over any server-dependent resource as I own the system. For example, imagine an app that always presents a log-in screen every time the app is launched, instead of worrying about inputting a correct username and password that would successfully authenticate a given user, why not simply making the mock API respond with success no matter the input? After all, this authentication step is not the focus of the feature I’m currently working on so I just want to get over it when launching the app in the simulator
- I get everything for free when the time comes to write my unit tests (and we all do unit testing, don’t we?)
"What do I gain with all of this? I save time during my workflow and this speed-up should be enough motivation to implement a mock API that lives “inside” the iOS app."
Mocking an API can be as easy as making a minor change in your codebase. No matter the architecture you’re using in your app (MVC, MVVM, VIPER, etc …), as long as you follow the basic software engineering principle of having a clear separation of concerns (by having a class that encapsulates all your API’s responsibilities) all you need to do is refactor your code so that you no longer rely on that API concrete class type but, instead, you develop towards an unknown type defined by a protocol.
The API layer as a protocol
Depending on your app’s architecture, you surely have the networking layer somehow isolated: whether it’s a class with everything in there, a simple struct containing reference to other API-related objects, some kind of wrapper around Alamofire or even your own HTTP client, you likely have it as a stand-alone object that hides the networking details from your UIViewControllers (speaking of which, it’s not a good idea for the UIViewController to know about the API layer but that’s a completely different topic which is out of the scope of this post). Now, instead of using a class or struct, you could use a protocol, something like this:
Then, the actual concrete type you had before would simply conform to this protocol and implement the required methods, exactly as it did before, encapsulating all the necessary logic to reach the actual APIAnalogously, the mock API object would also conform to this protocol but the specific implementation would just generate the mocked data:
The main idea with this refactor is that any class in your codebase that used to call the concrete API class should no longer do that and, instead, should just know that there’s a type conforming to the API protocol. With this change, you can call any method on the API protocol, no matter whether the concrete implementation is the real API or the actual mock.
"With a simple protocol-oriented mindset we just endowed our app with the ability to bypass all networking calls, which will be replaced by mocked data that we own and control."
As for how and when to actually decide on which concrete implementation to use, I opted for a runtime approach where, depending on the presence of a given launch argument passed in during app launch, the app will either use one or the other implementation. Imagine that I’m instantiating my API object in my UIViewController (which is never a very good idea… but, again, let’s leave it out of this discussion), I could simply do something like this:
I like going with the command-line argument, that way I can simply create a new scheme and have the given argument passed on app launch. As I won’t use this scheme to archive the app to the App Store I can rest assured that my codebase, by default, will target the real API but, for development purposes, it’s quite convenient to have this particular scheme that uses the mock version.
Mocking the data
When it comes to mocking the actual data, there’s a couple of options out there. On one hand, we can simply create our struct, array of structs or whatever type the closure takes as a parameter and simply call that closure with that parameter within the method’s implementation. On the other hand, we can take it further and include a utility that reads data from a local json file and parses it to the desired type accepted by that same closure. I actually prefer this way because I can simply drop whatever response I’d expect from the real API to a separate json file and modify the values at will. This is also easier to read if the data structure is somehow complex, with many nested key-value pairs.
The important thing to have in mind is, whichever path you choose to take in order to mock your data, you already have the data structure for it as it’s the same you use in your real API implementation. It doesn’t matter if you’re using a struct conforming to Codable or if you’re using something else from an external library, whatever you use to hold your data in one implementation of the API protocol is exactly the same data structure you’ll use in the other implementation. And we love keeping things DRY, don’t we?
Adding some flavor
The cool thing about this Mock API is that it’s super simple to add some tweaks that might prove to be extremely helpful during development. Is your network speed so fast that you can’t eagle-eye how you implemented your UIActivityIndicatorView? Well, let’s mock something like a 5-second delay so that we have enough time to see how our UI looks like when the app performs a network request:
This simple implementation just applies the same delay to every request implemented on the MockAPI object. 90% of the time I have it as zero-seconds because, like I said in the beginning, I just want to speed my workflow and I don’t want UIActivityIndicatorViews showing up when clicking around.
But feel free to get creative with it, instead of hardcoding it in the source code, why not adding a settings bundle to your development builds so that you or even a less technical savvy team member can set this value from the settings app instead?
What about when things fail? Does the app look and behave as expected when the server returns an error? Is a specific error being handled correctly? Perhaps in a given use case, there are distinct scenarios you want to handle and you want to mock every single one of them. Again, not a big deal, when using our Mock API:
For this specific request, where the state of our app can clearly assume two different twists, we just go through them and check how does our app behaves in which case. Remember, you aren’t hacking anything, you’re actually mimicking a pretty normal real-world scenario where, for whatever reason, the first request fails and your app should behave and be prepared to attempt a new one, which will then succeed. These edge-cases aren’t easy to test when you’re working with a remote API, even in a testing environment.
I have a sample project on Github demonstrating some of the points discussed here. It’s a really simple app that connects to Star Wars API or, if run under the proper scheme, will bypass that and use the Mock API implementation. In that case, it will display Alien movies instead, based on the json file that comes with the app. Please, disregard architecture or super-simplistic UI, it’s just an example app that focuses on the topic being discussed here.
Implementing a Mock API in an iOS app is a very straight-forward task.
My idea with this post is not to instruct you how to do it, even someone new to iOS and Swift can easily come up with the same concept once that person is versed on taking advantage of simple protocol-oriented programming techniques like this. Most importantly, I want to emphasize the value I gained when I started mocking the remote API on my own projects: I could forget about the backend, I couldn’t care less whether the backend team had implemented a given endpoint or not, I didn’t need to worry about knowing and accessing the backend stack to manipulate the data as I could do it directly in my app. Let me stress on this again: nowadays there’s hardly an iOS app without a backend server feeding data into a mobile app and being able to build it completely isolated from its existence it’s a benefit that will speed up your workflow.
However, there’s a big warning sign on this approach: the contract between the frontend and backend can’t be breached, otherwise, your app can crash or behave in a very unexpected manner. Just keep a close communication with the backend team, if they change the response they’ve agreed to send back to the client app, make sure you’re up-to-date with those changes because if you get too used to develop against a mock API all the time it’s really easy to forget that things might not actually work as expected when the app connects to the actual API.
I hope you learned the benefits you gain when implementing a mock API and if you’re taking it to your current project just let know how it worked for you!
João Carreira: "I am a Software Developer well-versed in the development and delivery of different iOS/web applications over the last 5 years; contributing immensely to 7 industry-grade apps on the AppStore. I have technical mastery in Swift, can also work with legacy Objective-C codebases, and popular iOS libraries like Alamofire, Kingfisher, SnapKit or Charts. I strive to deliver user-friendly products built on top of clean and testable architectures for clients."
João Carreira is the type of developer who is continuously learning and improving his skills.