Tiny Networking: Building Micro-Libraries in Swift

We were thrilled to welcome back Chris Eidhof of objc.io & UIKonf fame to the Swift meetup. In his previous talk, he told us about Functional Programming in Swift. In this one, Chris showed how you can quickly create libraries in Swift using simple function composition. One of our most acclaimed talks to date!

As usual, video & slides are synchronized. The video is also subtitled. You can find a blog version of the talk (with code samples) below.


Alamofire + Swift (0:00)

When Chris began working on a new project a couple months back, there was no CocoaPods Swift support. Alamofire had yet to provide Swift support for its networking functions, so Chris decided to experiment with a simpler way to parse results.

A tiny networking library (2:40)

The library is composed of one function and one data type. The function apiRequest takes four parameters: the baseURL of the API, the resource of A which describes the thing we are trying to get, followed by failure and completion blocks.

Get more development news like this

func apiRequest<A>(
   baseURL: NSURL,
   resource: Resource<A>,
   failure: () -> (),
   completion: A -> ()
)

The data type is this struct Resource<A>.The path describes the path of the resrouce, /api/users/log_in, which the method being one of the HTTP methods. There are some headers, a dictionary of string to string, a requestBody of type NSData, and the parse function.

struct Resource<A> {
    let path: String
    let method : Method
    let headers : [String:String]
    let requestBody: NSData?
    let parse: NSData -> A?
}

There are also a few helper functions. The typealias for JSONDictionary gets a dictionary from string to AnyObjects. The functions decodeJSON and encodeJSON decode and encode JSON, respectively. Decode takes NSData and tries to make a JSON dictionary out of that, while encode tries to do the reverse.

typealias JSONDictionary = [String:AnyObject]
func decodeJSON(data: NSData) -> JSONDictionary?
func encodeJSON(dictionary: JSONDictionary) -> NSData?

Usage (6:13)

The parse function, mentioned earlier, tries to create a string out of NSData. It tries to decode the JSON, and if successful, tries to look up the auth_token in the dictionary. If found, it converts it to string and returns the results as a string. If the data couldn’t be parsed to JSON, the auth_token was not there, or the auth_token was of the wrong type, then nil will be returned.

let parse : NSData -> String? = { data in
  if let jsonDict = decodeJSON(data) {
      return jsonDict["auth_token"] as? String
  }
return nil
}

let requestParameters = ["email": "[email protected]",
                         "password": "testtest"]
let jsonBody = encodeJSON(requestParameters)

With a set path and a constructed resource value, you can make the API request with the result of either a print failure or the printed authorizationToken.

let path = "api/users/sign_in"
let headers = ["Content-Type": "application/json",
               "Accept": "application/json"]
let resource = Resource(path: path, method: .POST,
                        requestBody: jsonBody,
                        headers: headers, parse: parse)

let baseURL = NSURL(string:"http://localhost:3000")!
apiRequest(baseURL, resource, {
      println("failure")
  }, { authorizationToken in
    println("Auth token \(authorizationToken)")
  })

Live Coding (7:56)

The way that I work in Swift is just starting out by playing around a little bit and calling some functions. You wrap them in an autofunction and you can keep refactoring and massaging this until you end up with lots of short, small, sweet functions. When I use Swift, I really try to use the compiler and let it do a lot of the work for me by writing broken code and seeing what the compiler tells me to do next.

Resources (36:15)

Q&A (37:10)

Q: What if your authorizationToken expires, is there a nice generic way to deal with that?
Chris: I think this is really dependent on your domain. One thing to make the code a little bit shorter is the API requestion function, which creates a data test to look at the response and the status code. It tries to parse the data into NSData, and the else clauses in my production codes will return a result or a very specific error. Person 1: You can save a refresh token with the authorizationToken so that you can refresh and get a new authorizationToken. It’s a nuisance, but hthe user doesn’t want to know about this and you need to do it for security.

Q: Is the password sending clear text?
Chris: One of the problems is that the password is sent over HTTP. In the production code, it’s sent over HTTPS, which is already a lot safer. However, I’m not a security expert so I don’t really know. There are definitely things you can do with things like encryption challenges, but unfortunately, I don’t feel authorized to answer anything like this.

Q: What is your opinion on having the code return an enum of a success or a failure, like an NSError?
Chris: I think it’s a preferable solution, and it’s fairly easy to make that change. What’s nicer about this solution is that it’s only a single function, so there’s less to worry about. It’s way clearer and it says the completion block is going to get called with either nil or a result. I really like how you can change the API in one place and still keep your wrappers.

Q: Why were you passing in empty dictionaries, and would it be possible to pass in optionals instead?
Chris: I don’t know, I think it depends on what you want to do. In today’s example, it might actually be nicer to pass in an optional instead. It depends on whether you consider an empty dictionary to be an empty response. I do try to avoid optionals whenever I can. You have to know there’s always going to be a value to get rid of an optional.

Q: Rather than having optionals, what about arrays? I read a blogpost about transforming Java code into functional style where they passed empty arrays instead of optionals.
Chris: This is also a way to think about optionals, right? Optionals are like arrays with a maximum of one element - they’re either nothing, or one element.


Chris Eidhof

Chris Eidhof is the author of many iOS and OS X applications, including Deckset and Scenery. He has also written extensively on the subject, from his personal blog to objc.io to a variety of books. He formerly ran UIKonf, and still runs frequently.