Testing network requests can be tricky. Generating mock data, handling HTTP routes, etc. Thing get complicated quickly. Today we'll check out a library from devlucky called Kakapo that can help us tame all this. Let's get started.
One of the best features of Kakapo is how easy it is start using. We can create a new Router for a domain, and start adding intercepted routes like this:
In the above example, we're returning static data. Let's kick things up a notch and return some dynamic data. This is another place Kakapo really shines:
Way back in Bite #93 we talked about creating a "router" for Alamofire that would generate URL requests from enum cases.
In most apps we'll likely end up with some version of that code to help communicate with the API for that particular app. It'd be great if there was some sort of "standard" way to write this code each time.
Today we'll look at a library called Moya that aims to provide this. Let's dive in. 🏊
Moya is a network abstraction layer that encapsulates calling Alamofire functions. It helps us avoid creating messy custom network abstractions in every project.
To use Moya in an app, we'll need an enum with a case representing each API endpoint we want to hit:
enumFirstOrder{caseAllTroops;caseTroop(String)}
Then we'll extend it to conform to Moya'sTargetTypeprotocol:
This is just a taste, Moya has a ton to offer including some fantastic enforcement of good testing practices.
In conforming to the TargetTypeprotocol, we're actually required to provde sampleData. We can also customize things further by providing an endpointClosure when creating our provider.
App Transport Security was introduced with iOS 9 and OS X 10.11. It aims to make the network calls we make from our apps more secure by enabling many best practices like TLS 1.2 and forward secrecy by default. Today we'll take a closer look, and learn how to disable it if needed.
App Transport Security makes support for Transport Layer Security 1.2 and forward secrecymandatory. It also requires certificates to have a SHA256+, ECC256+, or RSA2048+ signature. Don't worry if that sounds like gibberish, the idea here is that Apple will maintain ATS on an ongoing basis, keeping it always up-to-date with the latest security best practices and standards.
Apple is enforcing ATS by automatically "opting-in" the NSURLConnection, NSURLSession, and CFURL APIs (plus anything built on top of them).
This means these APIs will throw errors, and the connections will fail if we try to use them with a connection that doesn't meet all the requirements.
Unfortunately, not all connections we need to make in our apps will support ATS. In these cases, we'll need to tell the system to exempt these connections, and allow them to be made insecurely. We'll do this by adding some keys to our app's Info.plist:
We'll start by opening our Project's settings, then heading to the Info tab.
Then, we'll right click the list of keys, and choose Add Row. We'll use the inline plus buttons to continue adding and configuring rows until we end up with this:
There's also keys for allowing for lower minimum TLS versions, as well as not requiring forward secrecy.
It's encouraging to see Apple putting such an emphasis on securing our apps. App Transport Security is big step forward in making all of our apps safer for us and our users.
Building a good authentication system is a lot of work. Instead of starting from scratch, it'd be great if we could build on top of some existing popular service, and allow our users to log in with their existing account there.
Today we'll check out SimpleAuth from Caleb Davenport. It provides an extremely easy-to-use way to implement social sign-in inside our apps. Let's take a look:
SimpleAuth is built around the concept of Providers. In this context, a provider contains all the code needed to talk to individual services like Twitter, Facebook, etc.
Let's add support for signing in with Twitter to an app. We'll start by adding the pod to our Podfile, then run pod install.
pod'SimpleAuth/Twitter'
Then, we'll need a consumer key and secret from Twitter. We can get these by creating a new app on Twitter's developer portal.
Back in our Application Delegate, we'll configure SimpleAuth'sTwitter provider with our info:
Then we can grab their Twitter username out of a user dictionary. (Which contains keys like uid, image, etc.) SimpleAuth uses these names to abstract away the different attribute names each service uses to represent these values. For example, the field uid always holds a unique user identifier and is present on almost all providers.
SimpleAuth ships with a ton of built-in providers (Twitter, Facebook, Instagram, Tumblr, Dropbox, Foursquare, etc.). It also makes it very straightforward to create our own providers, just in case we ever need to. Neat!
NSURLQueryItem is a great little class that joined Foundation in iOS 8. It can help us compose NSURLs in a safer and more predictable way.
iOS and OS X developers have long become familiar with composing URLs in Cocoa. It can be… "interesting" at times.
We've all written a line or two of NSString-concatenation or stringWithFormat code to quickly create a URL. This works in a pinch, but we could easily introduce a bug by putting an & character in the wrong spot, or some other silly typo.
NSURLQueryItem can help! Let's look at how to use it along with NSURLComponents to compose an NSURL:
This is great for a few reasons. For example, instead of doing something silly like string replacing shudder to change a query parameter, we can instead operate on the components' queryItemsarray, then export the URL again by calling .URL. Additionally, with this technique, we can now more easily validate query parameters in URLs in our tests! Double-win. Neat!
Mobile devices can lose or change connectivity state suddenly and unexpectedly. It's important for us to make our apps respond well and degrade gracefully when this occurs.
Reachability describes how "reachable" the Internet is at a given time. Apple has long offered sample code for dealing with Reachability, but the process has always been a bit of a nuisance.
Today we'll look at a great little library from Ashley Mills called Reachability.swift that aims to make this less painful. Let's begin.
We'll start by trying to create a new Reachability object:
letreachability:Reachabilitydo{reachability=tryReachability.reachabilityForInternetConnection()}catch{print("ERROR: Unable to create Reachability")assumeConnectivity()}
Then we'll set some closures on it that will be called when the Reachability state changes. These will be called on a background queue, so we'll hop to the main queue before updating any UI.
reachability.whenReachable={reachabilityindispatch_async(dispatch_get_main_queue()){/* TODO */}}reachability.whenUnreachable={reachabilityindispatch_async(dispatch_get_main_queue()){/* TODO */}}
Now, we can tell the object to start listening for connectivity changes:
do{tryreachability.startNotifier()}catch{print("ERROR: Unable to start Reachability notifier")assumeConnectivity()}
Then stop it later with:
reachability.stopNotifier()
It's important to consider if our appeven needs this functionality.
If possible, we should try to avoid degrading the experience at all when connectivity goes away. If this isn't possible, for example if our app is streaming live video, we should respond to the change in Reachability, and tell the user why playback was interrupted.
Note: We looked at the simple closure syntax here, but Reachability.swift has great support for NSNotificationCenternotifications as well as Wifi vs. Cellular detection.
Continuing where we left off in Bite #93 with our Alamofire Router, today we'll take a look at creating a Custom Response Serializer.
These are the mechanisms through which the Alamofire HTTP library translates the HTTP responses it receives into types that are more friendly to work with.
Alamofire ships with a few built-in serializers for common things like raw data, text, JSON and property lists. Today we'll be taking a look at adding our own. Let's get started.
We want to make it super easy to translate Alamofireresponses into structs that conform to the Decodableprotocol from the Decodable JSON parsing library. (Bite #86).
First we'll extend Request and add a new genericresponseCollectionfunction that returns a Response struct.
Notice that in the generic part of the function definition we allow any type T that confirms to the Decodable protocol.
Now the fun part. We'll first serialize the response using the built-in JSON response serializer, then switch on its result, returning the failure case if it exists.
If the JSON was parsed successfully, we'll try to decode using Decodable.
Now we can try both our Routerand our new custom response serializer out together:
The responseCollectionfunction handles arrays of objects. Making a responseObjectserializer function to handle single object responses is almost identical. Simply replace all occurrences of [T] with T.
Alamofire 3.0 will be released soon so now is a good time to start looking at how best to build an API client for our app. We'll be covering different areas of this topic periodically over the next few weeks.
Today we'll start by looking at one part of the equation: routing.
We've talked about routing before in the context of iOS URL schemes and libraries like JLRoutes (Bite #62). This is a bit different. We'll be creating a Router type that will help us unify and simplify our code when we make requests by generating URL requests from enum cases.
We'll start by declaring a new enum type that conforms to the URLRequestConvertableprotocol (which comes from Alamofire). Then we'll define a few different cases that each correspond to an endpoint on our HTTP API. We'll add a computed route property that returns a tuple to translate from our enum cases into URL path and parameter values.
Finally, we'll finish conforming to URLRequestCovertable by adding a computed property that encodes our URL and parameters into an NSMutableURLRequest. Neat.
Now we can use our new Router to write some super clean Alamofirerequest code like this:
NSURLSession is the heart of networking in Foundation. Let's take a look at a few ways to use it.
The basic idea is we'll create a new NSURLSession, and then use it to create NSURLSessionTasks that will make the actual HTTP requests. We'll start by creating a new session:
We'll can use a data task like above, then use this function to grab the downloaded data. This will be called multiple times, so we'll need to keep an NSMutableData reference around to append to.
Imagine an app that downloads and plays movies. If a user begins downloading a movie, it'd be great if the file could continue downloading even if the user presses the home button and sends the app to the background. NSURLSessionDownloadTask can help. Let's take a look.
First we create a new background configuration and session. Then we use that session to create a new download task:
We can grab the downloaded data pretty easily, by implementing this function from NSURLSessionDownloadDelegate:
funcURLSession(session:NSURLSession,downloadTask:NSURLSessionDownloadTask,didFinishDownloadingToURLlocation:NSURL){print("Movie data written to \(location)")}
Last but not least, we'll need to implement a function from NSURLSessionTaskDelegate. In it we'll call finishTasksAndInvalidate on the session. This ensures everything is cleaned up and the session lets go of us (self) as its delegate.