Let’s Call REST APIs with AlamoFire, iOS 8 and Swift

Screen Shot 2014-10-26 at 2.22.38 PM

About Rex St John

I am a mobile software engineer and technical evangelist working for Intel’s Mashery subsidiary. Aside from attending dozens of hackathons, workshops and technical events, it is my job to help developers learn about and engage with REST APIs.

iOS 8 Networking With AlamoFire and Swift

In this article, we will be exploring the basics of REST APIs, learning how to create our first iOS 8 networking application using the AlamoFire library (and using the Swift programming language with Xcode 6). I have written this article to help beginners learn how to interact with REST APIs and learn the basics of iOS 8 development. The GitHub repo for this article is freely available by clicking here.

What are we building?

We are going to be building a basic app to make REST API calls to the JamBase API and display the results in a UISplitViewController. This particular chapter will be primarily concerned with building a very basic networking layer using AlamoFire.

What is an API? What does API stand for?

API stands for “Application Programming Interface.” The phrase API itself may be used in numerous contexts and have several different meanings. For the purposes of this article, when I say “API” I am referring specifically to REST APIs, an extremely commonly used method to retrieve data from a remote server over HTTP.

REST APIs are an extremely important topic for mobile applications developers because most applications use one (or many) REST APIs to retrieve the data they need for their users. If you are new to the world of programming, I suggest taking a few minutes to read through the REST API Tutorial. Thankfully, by following this guide, you will be learning critical skills needed by most mobile applications developers in the real world.

Project Setup and Configuration

master_detail

The first thing we are going to do is open Xcode 6 and create a new “Master-Detail” application. In iOS UIKit terms, what this will do is create what is called a “Universal Storyboard” which, through the magic of Interface Builder and Adaptive Layout, will allow us to create a single interface to use with both iPhone and iPad devices. Adaptive Layout is a new technology introduced by Apple to make interface-reuse less messy. In the past, multiple storyboards were often required (as well as convoluted switch statements) to perform the same goal. With Adaptive Layout, we can use new sizing and layout tools provided by Xcode to ensure that our users have the desired experience no matter what device they are using. There is more to it than that, if you are curious I’d take a look through this guide.

naming

You can name your project whatever you want, the key thing to note is that the Devices parameter is set to Universal as above.

 

startingUI

The end result of this setup will be a new XCode 6 projects with the above storyboard layout. What is going on here is that XCode has automatically created a UISplitViewController (which consists of a table with a detail view for each item in the table) for us by default. This UISplitViewController will form the basis of our application. For this chapter we won’t be doing much with this layout as we will be focusing our initial efforts on making REST API calls using AlamoFire.

Lets Install AlamoFire

AlamoFire is a networking library which will help make it easier for us to make calls to our JamBase API and then display the results on the screen. Following the AlamoFire installation instructions we will create a new Git Submodule and add it to our Xcode project. Follow these instructions for exact steps.

final

The key elements to ensure are…

  • Alamofire.framework’s xcode project is included in your main project
  • Alamofire.framework is added as a target dependency
  • You have created a Copy Files step (renamed to Copy Frameworks in the above example) and set Destination to “Framework.” Then, add Alamofire.framework files to the copy action.

Note: You will notice that AlamoFire references what is called a Git “submodule.” This effectively means that we are including  a reference in our Git repo to an external Git repository managed by someone other than us. Many iOS developers prefer to use an approach known as CocoaPods for handling and managing external dependencies like this but it seems the new nature of Swift has temporarily made it easier to use Git submodules for the time being (this may change in the future).

Introduction to Mashery and Mashery API Network

The Mashery API Network consists of nearly 50 public APIs any developer can us to make interesting mobile applications. In this guide, we will be using the JamBase API (from the Mashery API Network) to build our application and display local concerts nearby.

Mashery API Network allows you to rapidly register applications with nearly 50 APIs using a single sign-on. Once you have registered as a developer with the Mashery API Network, you can use your same sign-in details to access all of the publicly available APIs. Please take a moment to create a new Mashery API account and click the link in the email you will receive to verify your account.

JamBase

Once you are done, navigate to the JamBase developer portal and click the “Sign In” button in the upper right hand corner. Once you are signed in with your Mashery Developer account, select “My Account -> Applications -> Create A New Application” as in the above image. Fill out the provided form and submit it in order to receive your confidential developer key.

Important: You will need the key you will be provided to make API calls, which we will get into next.

 Lets Build Our Networking Layer

Cntrl + Click in the left-hand project pane and create a new Swift file. Name it “Networking.swift.”

new_swift_file

Open the “Networking.swift” file and type this in (make sure to replace YOUR_KEY_HERE with the provided JamBase API key):

import Foundation
import Alamofire

class Networking {
    
    // Get nearby events by a provided Zip Code
    class func getEventsNearby() {
        Alamofire.request(.GET, "http://api.jambase.com/events", parameters: ["zipCode": "95128","page":"0","api_key": "YOUR_KEY_HERE" ])
            .response { (request, response, data, error) in
                println(request)
                println(response)
                println(data)
                println(error)
        }
    }
}

I won’t go too deep into the topic of the Swift programming language, but what we have done here is create a new class called “Networking” and then added a “Class Level Method” (class func getEventsNearby) which we can call from other part of our code. To call this function, all we need to do is write “Networking.getEventsNearby();

To test that our new networking function works, copy + paste the above command into the “viewDidLoad” function of your MasterViewController.swift file and run your project. When you run the project, you should see the following in the console:

output

 

If any of the above is confusing, feel free to get the example project from the provided GitHub repo linked at the top of this article.

 Whats all this gibberish?

You may notice that a string of weird numbers has been rendered in the console as the “data” field for our HTTP response. The reason for this is that we have not properly handled the data output. Lets fix this issue by editing our network call to look like this (Make sure to swap in your JamBase key):

// Get nearby events by a provided Zip Code
    class func getEventsNearby() {
        Alamofire.request(.GET, "http://api.jambase.com/events", parameters: ["zipCode": "95128","page":"0","api_key": "YOUR_KEY_HERE" ])
            .responseJSON { (_, _, JSON, _) in
                println(JSON)
        }
    }

We are telling AlamoFire that we would like to see the response processed as JSON data. JSON is the most frequently used return format for REST API data so this shouldn’t be a surprise When we run our project now, we should see an output that looks more like this:

response

Dealing With Collections and Generic Objects

Now that we have managed to retrieve JSON, we now want to render these results back into generic Swift model objects. We do that by making use of the following code:

/**
* Response Object Serializer Extension
*/

@objc public protocol ResponseObjectSerializable {
    init(response: NSHTTPURLResponse, representation: AnyObject)
}

extension Alamofire.Request {
    public func responseObject(completionHandler: (NSURLRequest, NSHTTPURLResponse?, T?, NSError?) -> Void) -> Self {
        let serializer: Serializer = { (request, response, data) in
            let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
            let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
            if response != nil && JSON != nil {
                return (T(response: response!, representation: JSON!), nil)
            } else {
                return (nil, serializationError)
            }
        }
        
        return response(serializer: serializer, completionHandler: { (request, response, object, error) in
            completionHandler(request, response, object as? T, error)
        })
    }
}

/**
* Response Object Collection Extension
*/

@objc public protocol ResponseCollectionSerializable {
    class func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}

extension Alamofire.Request {
    public func responseCollection(completionHandler: (NSURLRequest, NSHTTPURLResponse?, [T]?, NSError?) -> Void) -> Self {
        let serializer: Serializer = { (request, response, data) in
            let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
            let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
            if response != nil && JSON != nil {
                return (T.collection(response: response!, representation: JSON!), nil)
            } else {
                return (nil, serializationError)
            }
        }
        
        return response(serializer: serializer, completionHandler: { (request, response, object, error) in
            completionHandler(request, response, object as? [T], error)
        })
    }
}

All this may look highly abstract and intimidating, but basically what we are doing here is teaching Swift how to render our JSON collection of “Event” objects into useful models. We will now modify our network call to look like this:

final class Networking {
    
    // Get nearby events by a provided Zip Code
    class func getEventsNearby() {
        Alamofire.request(.GET, "http://api.jambase.com/events", parameters: ["zipCode":"98102", "page":"0","api_key":"YOUR_KEY_HERE" ]).responseCollection  { (_, _, events: [Event]?, _) in
                println(events)
        }
    }
}

And introduce some generic model objects:

/**
* Simple Model Objects
*/

/**
* This represents a collection of JamBase API Event models.
*/
final class Event: ResponseObjectSerializable, ResponseCollectionSerializable {

    let ticketUrl: String
    let venue: Venue
    let artists: [Artist]
    let date: String
    
    required init(response: NSHTTPURLResponse, representation: AnyObject) {
        self.ticketUrl = representation.valueForKeyPath("TicketUrl") as String
        self.venue = Venue(response:response, representation: representation.valueForKeyPath("Venue")!)
        self.date = representation.valueForKeyPath("Date") as String
        self.artists = []
        
        for artistRep in representation.valueForKeyPath("Artists") as [AnyObject]{
            self.artists.append(Artist(response: response, representation: artistRep))
        }
    }
    
    class func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Event] {
        var events:[Event] = []
        for eventsRep in representation.valueForKeyPath("Events") as [AnyObject] {
            events.append(Event(response: response, representation: eventsRep))
        }
        return events
    }
    
    func toDic() -> Dictionary<String,AnyObject> {
        return ["ticketURL": self.ticketUrl, "venue":self.venue, "artists":self.artists, "date":self.date]
    }
}

final class Artist: ResponseObjectSerializable {
    let name: String
    
    required init(response: NSHTTPURLResponse, representation: AnyObject) {
        self.name = representation.valueForKeyPath("Name") as String
    }
}

final class Venue: ResponseObjectSerializable {
    let name: String
    let city: String
    let address: String
    let country: String
    let zipCode: String
    let state: String
    let stateCode: String
    
    required init(response: NSHTTPURLResponse, representation: AnyObject) {
        self.name = representation.valueForKeyPath("Name") as String
        self.city = representation.valueForKeyPath("City") as String
        self.country = representation.valueForKeyPath("Country") as String
        self.zipCode = representation.valueForKeyPath("ZipCode") as String
        self.state = representation.valueForKeyPath("State") as String
        self.address = representation.valueForKeyPath("Address") as String
        self.stateCode = representation.valueForKeyPath("StateCode") as String
    }
}

Now, when we run our basic implementation, our Swift networking “engine” will render the JSON returned from the JamBase API into useful Swift “Event” model objects.

source code click here.