Getting started with Alamofire and SwiftyJSON iOS 05.11.2019

SwiftyJSON

If you have evere used JSONDecoder or JSONSerialization you know that they require a number of lines of code:

  • With JSONDecoder, because the structure of the raw JSON data differs significantly from the model’s structure in code, you had to define your own decoding logic in your custom implementation of Decodable, missing out on the convenience of automatically generated decoding.
  • With JSONSerialization, due to Swift’s type safety, the code involved in extracting information from JSON data can also be verbose. With the data returned from the JSONSerialization class, it’s then necessary to unwrap and downcast every object as you traverse the data hierarchy.

Several third-party solutions out there address this problem and try to reduce the number of lines required to extract data from JSON. Probably the most popular at present is SwiftyJSON.

To install our pods, we need to initialize our Podfile. Your Podfile should look something like this:

target 'Sandbox' do
  use_frameworks!

  pod 'Alamofire', '~> 5.0.0-rc.3'
  pod 'SwiftyJSON', '~> 4.0'

end

You have to execute the following to fetch the latest version of the added framework:

pod install

Don't forget to use the workspace project instead of the regular project.

Example of usage

import SwiftyJSON

let json = try? JSON(data: networkData)
if let title = json["book"]["title"].string {
    print(title)
}

if let year = json["book"]["year"].int {
    print(year)
}

if let authors = json["items"][0]["authors"].arrayObject as? [String] {
    print(authors)
}

Using SwiftyJSON, you can now drill down to the data you’re after using familiar dictionary and array syntax. To finally extract a foundation type, use the property relevant to the type. For example, to extract a String, Double, Int, or Array you would use the properties string, double, int, or arrayObject. If you prefer a default value to an optional, add the suffix Value to the property, that is, stringValue, doubleValue, intValue, or arrayValue. The longer .intValue returns a non-optional Int with sensible defaults, such as 0, "" or [].

Here’s an example that shows how you can use SwiftyJSON to read JSON data:

let numberOfBooks = json["num"].intValue
print(numberOfBooks)

for book in json["books"].arrayValue {
    if  let isbn = book["isbn"].string,
        let title = book["title"].string {
        print("\(title): \(isbn)")
    }
}

Alamofire

To handle the communication with a server, we have to make requests. Each request is a separate action. We should use URLSession to manage all HTTP requests.

At some point, you will need a nice and easy way to consume web resources, for example, to download images, read data from a remote location, or to upload data to a backend. These are all valid problems which need a good solution. We can develop everything from scratch using the tools part of iOS - NSURLDownload, CFHTTPStream, NSURLSession, or NSURLConnection. Each of these should be used in different situations, but you will need to write the very same boilerplate code to prepare everything and to handle all resources. Is this necessary every time? No. You can simply use Alamofire. This is an HTTP networking library which is really popular. It is written in Swift and is pretty easy to use.

Use this powerful project in the following cases:

  • For making requests, response handling, and response caching
  • For supporting different HTTP methods (PUT, GET, POST, PATCH, DELETE)
  • Parameters encoding, HTTP headers, and authentication
  • Routing requests, adapting, and retrying
  • Session management
  • Uploading and downloading big files Security and reachability

We’ll create a new file called Post.swift. We want this object to be able to initialize itself from the JSON object returned by Alamofire. We can create a new initializer that starts to access the data:

import SwiftyJSON

struct Post {
    var id: Int
    var userId: Int
    var title: String
    var body: String

    init(data: Any) {
        let json = JSON(data)

        self.id = json["id"].int ?? 0
        self.userId = json["userId"].int ?? 0
        self.title = json["title"].string ?? "-"
        self.body = json["body"].string ?? "-"
    }
}

Now, we can do the API call using the Alamofire framework:

class FirstViewController: UIViewController {
    func doRequest(url: URL, completion: @escaping (Any?, Error?) -> ()) {
        AF.request(url).responseJSON { response in
            switch response.result {
            case .success(let result):
                completion(result, nil)
            case .failure(let error):
                completion(nil, error)
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1/")!

        doRequest(url: url) { (response, error) in
            print("\(response)")
            print("\(error)")
        }
    }
}

The code makes a request and the handling is done in a closure.

Alamofire is pretty powerful and it has different types of requests. Here, we are using the responseJSON This is the one which we should use when the format of the response is JSON.

We can always use a classical response. There are a few more:

  • responseString. Converts the data sent to a String.
  • responseData. Converts the data to a data type
  • responsePropertyList. Converts the data to a plist (object type)
  • responseDecodable. Converts the data to a object.

It's pretty easy to make different requests. The method type is passed to the request function. Also, the parameters and encoding could be specified. The library provides a powerful way of customization. The final code is clean and concise.

We can do the API call using the Alamofire framework and convert response to object:

AF.request(url).responseDecodable(of: Movie.self) { response in
    guard let movie = response.value else { return }
    print(movie)
}