4 Essential Swift Networking Tools for Working with REST APIs

Facebook
X
LinkedIn

How to integrate AlamoFire with Multipart, SwiftyJSON, Image Caching, and Image Response Serialization to simplify your networking code

patch panel

Networking is hard, I’ll be the first to admit. It was an area I tended to avoid while learning Swift, even though I used to be a network administrator, managed web servers and created mobile APIs. At the suggestion of the Columbia iOS meetup group, I put together a Swift Network Starter Kit that integrates some online solutions into a template. This is a working example that integrates and demonstrates a few networking features useful for most projects. Use this to jump start your next test project in Swift. Update: A lot has changed with Swift and Alamofire. Be sure to check out this updated blog post: Scrolling in Two Directions like Netflix, Part 2: Making API Calls which makes calls to IMDB using Alamofire, AlamofireImage, and SwiftyJSON.

1. AlamoFire

alamofire logo AlamoFire is the networking library used in the template. It could take some time wrapping your head around AlamoFire due to the complex nature of networking, but this is a library worth learning. Why use AlamoFire? First, it simplifies your network call syntax. Requests are chained to responses, so flow control is much easier to reason about than callbacks. Visually, it reminds me of Promises in JavaScript. Using this library over the API will make your code more compact and easier to read.
let url = "https://itunes.apple.com/us/rss/topfreeapplications/limit=10/json"
request(.GET, url)
    .responseJSON { (_, _, data, _) in
        println("data:: (data)")
    }
AlamoFire also makes it easier to pass data back and forth. Use parameter encoding to convert a Swift dictionary to URL parameters for GET, or a JSON body for POST. In the response, you can choose a JSON format which saves you the hassle of converting it yourself.
let params = [ "foo" : "bar" ]
request(.POST, "http://httpbin.org/post", parameters: params, encoding: .JSON)
    .responseJSON { (_, _, data, _) in
        println("data:: (data)")
    }
As you start adding more API endpoints, you end up with a lot of boilerplate code. AlamoFire has something called an URLRequestConvertible, which lets you generate requests programmatically. You can consolidate all your API URL endpoints into one place, giving you a single point of change for when you need to tweak a timeout or add an auth token. Here’s what a simple router looks like:
enum Router: URLRequestConvertible {
    case FetchTopFree()
    var URLRequest: NSURLRequest {
        let (verb: String, path: String, parameters: [String: AnyObject]?) = {
            switch self {
            case .FetchTopFree():
                return ("GET", "https://itunes.apple.com/us/rss/topfreeapplications/limit=10/json", nil)
            }
        }()
        let URL = NSURL(string: path)!
        let URLRequest = NSMutableURLRequest(URL: URL)
        URLRequest.HTTPMethod = verb
        switch self {
        default:
            return ParameterEncoding.URL.encode(URLRequest, parameters: parameters).0
        }
    }
}
AlamoFire also supports Multipart now. Until recently, you had to fall back to AFNetworking, or build the request manually (which is not fun). I included a basic working example in the template, but the AlamoFire source code is a good resource if you need to dig into tailoring the call for your own API.
func createMultipart(image: UIImage, callback: Bool -> Void){
    // use SwiftyJSON to convert a dictionary to JSON
    var parameterJSON = JSON([
        "title": "foo",
        "description": "bar"
    ])
    // JSON stringify
    let parameterString = parameterJSON.rawString(encoding: NSUTF8StringEncoding, options: nil)
    let jsonParameterData = parameterString!.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
    // convert image to binary
    let imageData = UIImageJPEGRepresentation(image, 0.7)
    upload(
        .POST, 
        URLString: "http://httpbin.org/post",
        // you can swap out the http verb and URLString (above) for a URLRequestConvertible
        //Router.CreateMultipart(),
        multipartFormData: { multipartFormData in
            // fileData: puts it in "files"
            multipartFormData.appendBodyPart(fileData: jsonParameterData!, name: "goesIntoFile", fileName: "json.txt", mimeType: "application/json")
            multipartFormData.appendBodyPart(fileData: imageData, name: "file", fileName: "iosFile.jpg", mimeType: "image/jpg")
            // data: puts it in "form"
            multipartFormData.appendBodyPart(data: jsonParameterData!, name: "goesIntoForm")
        },
        encodingCompletion: { encodingResult in
            switch encodingResult {
            case .Success(let upload, _, _):
                upload.responseJSON { request, response, data, error in
                    let json = JSON(data!)
                    println("json:: (json)")
                    callback(true)
                }
            case .Failure(let encodingError):
                callback(false)
            }
        }
    )
}

let appleImage = UIImage(named: "apple")
createMultipart(appleImage!, callback: { success in
    if success { }
})

2. SwiftyJSON

pyramid of doom if let code Dreaded if-let pyramid of doom when unwrapping JSON objects Handling JSON in Swift is not pretty. You end up with an if-let pyramid of doom as you painstakingly peel back the layers of your JSON response. Fortunately, SwiftyJSON takes care of dynamic typing for you. With this library, you can safely pluck data from deeply nested JSON responses using compact, readable syntax.
let json = JSON(data!)
let label = json["feed"]["entry"][0]["im:name"]["label"].stringValue
println("label:: (label)")
// (console) label:: Minecraft: Pocket Edition
To learn more, I recommend this helpful Ray Wenderlich tutorial on working with JSON.

3. Image Caching

There’s a handful of image caching libraries out there to choose from. But there’s actually a very simple and stable implementation from NSHipster that leverages features already built into the API. This is the approach we use in this template. Just put this in your AppDelegate’s didFinishLaunchingWithOptions:
let URLCache = NSURLCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 20 * 1024 * 1024, diskPath: "cachedResponse")
NSURLCache.setSharedURLCache(URLCache)
And call this to cache your image response:
let cachedURLResponse = NSCachedURLResponse(response: response!, data: (data as NSData), userInfo: nil, storagePolicy: .Allowed)
NSURLCache.sharedURLCache().storeCachedResponse(cachedURLResponse, forRequest: request)

4. Image Response Serializer

If you find yourself pasting the same snippet of code every time you download an image, an Image Response Serializer could be the right tool for you. For example, you can cache the image after verifying that it downloaded completely. You can also convert the binary response to an UIImage for easy consumption.
extension Request {
    class func imageResponseSerializer() -> Serializer {
        return { request, response, data in
            if data == nil || response == nil {
                return (nil, nil)
            } else {
                if let contentLength = response?.allHeaderFields["Content-Length"] as? String {
                    if let data = data {
                        if contentLength == "(data.length)" {
                            let image = UIImage.safeImageWithData(data)
                            let cachedURLResponse = NSCachedURLResponse(response: response!, data: (data as NSData), userInfo: nil, storagePolicy: .Allowed)
                            NSURLCache.sharedURLCache().storeCachedResponse(cachedURLResponse, forRequest: request)
                            return (image, nil)
                        }
                    }
                }
                return (nil, nil)
            }
        }
    }

    func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self {
        return response(serializer: Request.imageResponseSerializer(), completionHandler: { (request, response, image, error) in
            completionHandler(request, response, image as? UIImage, error)
        })
    }
}

request(.GET, appImageURL)
    .responseImage() { (request, _, image, error) in
        if error == nil && image != nil {
            cell.imageView?.image = image
        }
    }
This Ray Wenderlich tutorial not only covers Image Response Serializers, but it’s a great resource for learning AlamoFire as well. Note that UIImage is not thread-safe, so you’ll also want to include some protective code in your serializer so you don’t crash whenever it tries to process more than one image at a time.
private let imageSync = NSLock()
extension UIImage {
    static func safeImageWithData(data:NSData) -> UIImage? {
        imageSync.lock()
        let image = UIImage(data:data)
        imageSync.unlock()
        return image
    }
}

Check out our repo

Our Swift Network Starter Kit is available on GitHub. It pulls the top ten free and paid apps from the iTunes API. There are also Post and Multipart buttons that show the payload reflected back from httpbin.org. The README has instructions on integrating it into your own project, and explains the code in more detail. Demo of the Post and Multipart responses logged to the console: network demo Use it as a starter template for your own test projects, or just pull out bits and pieces you find useful. We’re open to any suggestions on how to make it better and easier to use, so feel free to add your thoughts to the comments. Like this post? Please share it below! Then follow us on Twitter @thorntech and join our mailing list for future updates. Patch Panel Image courtesy of Brent Hensarling on Flickr.

Related Posts

Try SFTP Gateway Free for 14 Days — No Server to Stand Up, No Credit Card

Trying self-hosted software normally costs you an afternoon before you’ve even decided you want it. Spin up a VM, open the right ports, work through the marketplace listing’s prerequisites, and hope the image boots the way the documentation says it will. That setup tax is

SFTP Gateway 3.9: More Control, Better Performance

A client opens an SFTP connection and never lets go. Maybe it’s a scheduled job that lost its network path mid-transfer, maybe it’s a misconfigured script retrying without backing off; either way, the connection sits open, holding a slot on your server indefinitely. Multiply that

Introducing the SFTP Gateway Product Support Lifecycle

SFTP Gateway now publishes a written Product Support Lifecycle and a Version Support Calendar for every cloud marketplace, so you always know exactly how long your version stays patched, and when. Thorn Technologies is introducing the SFTP Gateway Product Support Lifecycle: a written commitment to