Looking for

Wednesday, July 30, 2014

Http Stubs: let's go Swift Swizzling!

Blog Update 20th August 2014: This initial post was tested using Xcode6 beta4, with Xcode beta5 Swizzling was broken. From release notes "Dynamic dispatch can now call overrides of methods and properties introduced in class extensions, fixing a regression introduced in Xcode 6 beta 5. (17985819)!" See stackoverflow question for more details

Happy beta6 Swifting!

Last weeks, I was delving into http and Swift with my friend Christos. To write robust code, we need unit tests of course and to isolate testing layer: you need a HTTP stub.

When we worked with Objective-C, we used to mock http calls with OHTTPStubs. So we thought we could carry on using it with our brand new Swift code. After all, Swift and Objective-C are interoperable. However, there are subtle differences with iOS8 and when trying to use OHTTPSubs obj-c lib with Swift code, we ran into this issue (merged now!).

But when you delve into some neat code, then the urge of writing your own is showing up... It wasn't long before my friend Christos came up with AGURLSessionStubs a http stub written entirely in Swift.

With this blog post, I'd like to share with you the inside of stubbing http library. Let's dig under the hood to see how http mock are implemented in Objective-C and how we can apply the same concepts (with some restrictions) in Swift.

How can I switch between stub and real implementation? One way of doing it would be to inherit and implement NSURLSessionConfiguration, another way would be to swap method implementation of NSURLSessionConfiguration.defaultSessionConfiguration() with mock swizzle_defaultSessionConfiguration() which contains code to register our mocked NSURLProtocol class.

Method swizzling

The runtime dynamic method replacement technique known as method swizzling uses Objective-C runtime meta-programming to swap methods at runtime.
Since we have interoperability Swift/Objective-C, if the object you want to mock inherits NSObject (in our scenario with NSURLSessionConfiguration, this is the case), you can use runtime meta-programming.
Here is how to swap 2 methods contents.

In this example we simply swap the content of method from defaultSessionConfiguration to swizzle_defaultConfiguration. You can simply defined swizzle_defaultSessionConfiguration as an extension of NSURLSessionConfiguration:

The trick is when you call NSURLSessionConfiguration.defaultSessionConfiguration() you actually call NSURLSessionConfiguration.swizzle_defaultSessionConfiguration() and vice versa. So the first call line 5 is not a recursive call but the call to the original method!
From a user point of view, here is the way to write a test:

1. line16-19: you need to instantiate your stub and tell runtime: go swizzling from now on when you call NSURLSessionConfiguration.defaultSessionConfiguration() I really mean NSURLSessionConfiguration.swizzle_defaultSessionConfiguration() because this one instantiate my mock NSURLProtocol and put it before all the other protocols so i can stub http calls now.
2. line 2-14: stub your response
3. line 25-33: do you work with NSURLSession as usual
4. line 27-28: assert
5. in teardown method don't forget to remove your stub with StubsManager.removeAllStubs()