Active Filters: Networking

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:

let router = Router.register("http://littlebitesofcocoa.com")

router.get("/bites") { request in
  return ["id" : 1, "title": "#1: View Controller Initialization 🚀"]
}

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:

let db = KakapoDB()
db.create(Bite.self, number: 10)

router.get("/bites") { request in
  return db.findAll(Bite.self)
}

We can use this functionality to not only test network requests, but even to "stub out" responses before fully implementing backend services. Neat!

Once we've wired up some routes, we can make our network requests like normal:

let URL = NSURL(string: "http://littlebitesofcocoa.com/1")!

session.dataTaskWithURL(URL) { (data, _, _) in
  // handle response
}.resume()

We can even wire Kakapo up to Alamofire (Bite #93) like this:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.protocolClasses = [KakapoServer.self]
let manager = Manager(configuration: configuration)

This barely scratches the surface of what Kakapo has to offer.

From advanced dynamic mocking and database functionality, great routing, and a squeaky clean API, it's definitely worth a look.

Learn more about Kakapo at git.io/kakapo

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:

enum FirstOrder { case AllTroops; case Troop(String) }

Then we'll extend it to conform to Moya's TargetType protocol:

extension FirstOrder: TargetType {
  var baseURL: NSURL {
    return NSURL(string: "https://api.firstorder.galaxy.far.away")!
  }

  var path: String {
    switch self {
    case .AllTroops: return "/troops"
    case .Troop(let id): return "/troops/\(id)"
    }
  }

  var method: Moya.Method { return .GET }

  var parameters: [String: AnyObject]? {
    switch self {
    case .AllTroops: return ["sort": "recent"]
    default: return nil
    }
  }

  var sampleData: NSData {
    switch self {
    case .AllTroops:
      return "[{\"id\": \"FN-2187\"}]".dataUsingEncoding(NSUTF8StringEncoding)!
    case .Troop(let id):
      return "{\"id\": \"\(id)\"}".dataUsingEncoding(NSUTF8StringEncoding)!
    }
  }
}

Finally, we'll create a new Moya provider using our enum and make it available globally:

let FirstOrderAPI = MoyaProvider<FirstOrder>()

Now we can make requests like this:

FirstOrderAPI.request(.Troop("FN-2187")) { result in
  switch result {
  case .Success(let response):
    do {
      let json = try response.mapJSON()
      // TODO: Parse JSON
    } catch(let error) { handleError(error) }
  case .Failure(let error): handleError(error)
  }
}

This is just a taste, Moya has a ton to offer including some fantastic enforcement of good testing practices.

In conforming to the TargetType protocol, we're actually required to provde sampleData. We can also customize things further by providing an endpointClosure when creating our provider.

More info about Moya can be found at git.io/moya

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 secrecy mandatory. 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's Twitter provider with our info:

SimpleAuth.configuration()["twitter"] = [
  "consumer_key": "12345REDACTED",
  "consumer_secret": "ABCDETOPSECRET"
]

Finally, we can sign a user in like this:

SimpleAuth.authorize("twitter") { userDictionary, error in
  User(username: userDictionary["nickname"]).persist()
}

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!

More info about SimpleAuth can be found at git.io/simpleauth.

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:

let components = NSURLComponents()

components.scheme = "https"
components.host = "api.spaceshipapp.com"
components.path = "/ships"

components.queryItems = [
  NSURLQueryItem(name: "start", value: "40"),
  NSURLQueryItem(name: "max_results", value: "20")
]

let requestURL = components.URL

NSURLComponents is quite a useful class on its own that can parse and assemble URLs based on the RFC 3986 standard.

So far we've only created components from scratch, but we could also get the components of an existing NSURL like this:

let components = NSURLComponents(
  URL: NSURL(string: "https://lboc.me?page=2")!,
  resolvingAgainstBaseURL: true
)

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' queryItems array, 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!

Thanks to Matthew Bischoff for suggesting today's topic! Send your topic suggestion to hello@littlebitesofcocoa.com

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:

let reachability: Reachability

do {
  reachability = try Reachability.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 = { reachability in
  dispatch_async(dispatch_get_main_queue()) { /* TODO */ }
}

reachability.whenUnreachable = { reachability in
  dispatch_async(dispatch_get_main_queue()) { /* TODO */ }
}

Now, we can tell the object to start listening for connectivity changes:

do { try reachability.startNotifier() } catch {
  print("ERROR: Unable to start Reachability notifier")
  assumeConnectivity()
}

Then stop it later with:

reachability.stopNotifier()

It's important to consider if our app even 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 NSNotificationCenter notifications as well as Wifi vs. Cellular detection.

More info about Reachability.swift can be found at git.io/reachability

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 Alamofire responses into structs that conform to the Decodable protocol from the Decodable JSON parsing library. (Bite #86).

extension Alamofire.Request {
  public func responseCollection<T: Decodable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
    let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in

      guard error == nil else { return .Failure(error!) }

      let result = Alamofire
        .Request
        .JSONResponseSerializer(options: .AllowFragments)
        .serializeResponse(request, response, data, error)

      switch result {
      case .Success(let value):
        do {
          return .Success(try [T].decode(value))
        } catch {
          return .Failure(Error.errorWithCode(.JSONSerializationFailed,
            failureReason: "JSON parsing error, JSON: \(value)"))
        }
      case .Failure(let error): return.Failure(error)
      }
    }

    return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
  }
}

First we'll extend Request and add a new generic responseCollection function 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 Router and our new custom response serializer out together:

Alamofire.request(LBOC.Router.Bites)
  .responseCollection { (response: Response<[LBOC.Bite], NSError>) in
    guard response.result.error == nil else { return }

    let bites = response.result.value

    print(bites)
}

The responseCollection function handles arrays of objects. Making a responseObject serializer 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.

enum Router: URLRequestConvertible {
  static let baseURL = NSURL(string: "https://littlebitesofcocoa.com")!

  case Bites
  case Bite(Int)
  case BitesTagged(Int)

  var URL: NSURL { return Router.baseURL.URLByAppendingPathComponent(route.path) }

  var route: (path: String, parameters: [String : AnyObject]?) {
    switch self {
      case .Bites: return ("/", nil)
      case .Bite (let biteID): return ("/\(biteID)", nil)
      case .BitesTagged(let tagID): return ("/", ["t": tagID])
    }
  }

  var URLRequest: NSMutableURLRequest {
    return Alamofire
      .ParameterEncoding
      .URL
      .encode(NSURLRequest(URL: URL), parameters: (route.parameters ?? [ : ])).0
  }
}

We'll start by declaring a new enum type that conforms to the URLRequestConvertable protocol (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 Alamofire request code like this:

Alamofire.request(Router.Bites)
  .responseObjects { (response: Response<[Bite], NSError>) in
    let bites = response.result.value
    print(bites)
  }

The responseObjects custom response serializer converts JSON to our own custom struct types. We'll look at how to create it in the near future.

We can also access raw URL requests like this:

let request = Router.Bite(93).URLRequest

Topics

#78: NSURLSession Basics 🔗

Topics

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:

let session = NSURLSession(
  configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
  delegate: self,
  delegateQueue: nil
)

Now we'll use the session to create different tasks to do our bidding:

Call HTTP API

let url = NSURL(string: "http://api.apple.com/releaseSchedule")!
let task = session.dataTaskWithURL(url)
task.resume()

Download Data

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.

func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData)

Upload Data

Here we're uploading an NSData. There's also another form of uploadTaskWithRequest that accepts a file URL to upload from.

let request = NSMutableURLRequest(
  URL: NSURL(string: "http://api.someservice.com/upload")!
)

request.HTTPMethod = "POST"

let task = session.uploadTaskWithRequest(request, 
  fromData: dataToUpload)

task.resume()

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:

class ViewController: UIViewController, NSURLSessionDownloadDelegate, NSURLSessionTaskDelegate {
    func downloadMovieFile() {
    let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("downloads-session")
    let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)

    let task = session.downloadTaskWithURL(NSURL(string: "http://supersecret.disney.com/movies/episodeVII.mp4")!)
    task.resume()
  }
}

We can grab the downloaded data pretty easily, by implementing this function from NSURLSessionDownloadDelegate:

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: 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.

func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
  if let error = error { print("Download Error: \(error)") }
  session.finishTasksAndInvalidate()
}
Page 1 of 2