Cant find the role you’re looking for?
Sign up to be the first to know about future job openings at GetYourGuide
Stay notified of new jobsCopyrighted 2008 – 2023 GetYourGuide. Made in Zurich & Berlin.

Senior iOS Engineer, Otavio Cordeiro, focuses on making sure people love our product. He knows testing in Swift can be a challenge, especially when using type methods. But he's got a solution.
Like them or not, type methods, aka class methods or static methods, are heavily used in Swift and part of our daily lives as developers.
From analytics trackers to requesting system permissions, we’ve all had to face type methods from external libraries in which we had no control over. Testing code that interacts with them might seem hard without using method swizzling, but fortunately, this doesn't always have to be the case.
Let's suppose there's a class called APIWrapper, which implements a type method that requests search results from an endpoint.

The method takes a query, String, and a completion block, (Result<[String], Error>) -> Void, which is triggered once the request finishes. Its internal implementation doesn't really matter since it could be from an external framework or generated by a code generator from the API specification.

But testing the part of the application that interacts with it isn't that obvious. Especially when we want to:
The snippet below shows SearchResultsTableViewDataSource, a UITableViewDataSource subclass which uses the API wrapper to populate the table view when the network request completes. Testing it triggers a real network request and doesn't provide what's needed to test the query passed to the wrapper and the different behaviors when the result returns.

If APIWrapper had an instance method instead of a type method, two easy and obvious solutions would emerge: protocols and subclasses. Using either/or it would be possible to replace the wrapper with a test double that captures the query parameter and completion block for verifying expectations.
In the example above, the API wrapper interface can't be changed and has to stay as a type method, therefore the easiest way to overcome this is via dependency injection. In this case, method injection via object initialization
The code below stores a method which matches the qualified symbol name of the wrapper method, using it to perform the search. In order to simplify the SearchResultsTableViewDataSource interface, the wrapper's search method is assigned using a default parameter value.

Finally, testing becomes trivial. The injected method is used to capture the query term and the completion block passed to the API wrapper. Once captured, these can be used to verify expectations.

More importantly, not only does this method work with type methods but also with free functions, such as SecItemAdd(_:_:) used to add items to the Keychain.
{{divider}}
Are you interested in joining the Engineering team at GetYourGuide? Take a look on our Tech Radar to check out the technologies we're using.

