SwiftyJSON
If you have evere used JSONDecoder
or JSONSerialization
you know that they require a number of lines of code:
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.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:
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 typeresponsePropertyList
. 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) }