When we talk about how to parse JSON with Swift we think about dictionaries and ugly code getting to the values of a REST API response. This processs can be made a lot cleaner and structured using a simple library and a REST response wrapper class.
Some time ago I had the post How to Make REST API Calls and Parse JSON with Swift, which was for an older Swift version and also using an older SwiftyJSON implementation. Today I will show you how to parse JSON with Swift 2.2 using SwiftyJSON through cocoapods and the Randomuser API for a dummy JSON response.
Setup the app and cocoapods
We start with a clean App so start Xcode, go ahead and Create a new Project and select the Single View Application. Insert your credentials and whatever name you like and finish the process.
As said before, this time we will use cocoapods which is a dependency manager for iOS libraries to install a pod called SwiftyJSON. This library helps us to parse JSON with Swift a lot easier.
To install cocoapods, follow the instruction on the official cocoapods website. Afterwards, create a Podfile at the rot of your blank project and insert:
platform :ios, '8.0' use_frameworks! target 'devdactic-rest' do pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git' end
This is a very simple Podfile, we just specify the target name (which was in my case devdacti-rest) and the Pod we want to install plus the git source. Save this file and run pod install
from your command line.
If this finishes successful, please use the *.workspace file from now on as this includes your original target and the Pods target.
Note: We have to specify use_frameworks! in our Podfile because SwiftyJSON is a Swift Pod.
If you want all of the code of this tutorial, grab your complete package below!
The final app with some dummy data will look like this:
Creating our REST Connection Manager
I love having all of my REST calls in one place, so inside your project create a new Swift file called RestApiManager.
In this class we will talk to the Randomuser API and see how to perform a simple GET and POST request with Swift. Open your created file and insert:
import SwiftyJSON typealias ServiceResponse = (JSON, NSError?) -> Void class RestApiManager: NSObject { static let sharedInstance = RestApiManager() let baseURL = "http://api.randomuser.me/" func getRandomUser(onCompletion: (JSON) -> Void) { let route = baseURL makeHTTPGetRequest(route, onCompletion: { json, err in onCompletion(json as JSON) }) } // MARK: Perform a GET Request private func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) { let request = NSMutableURLRequest(URL: NSURL(string: path)!) let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in if let jsonData = data { let json:JSON = JSON(data: jsonData) onCompletion(json, error) } else { onCompletion(nil, error) } }) task.resume() } // MARK: Perform a POST Request private func makeHTTPPostRequest(path: String, body: [String: AnyObject], onCompletion: ServiceResponse) { let request = NSMutableURLRequest(URL: NSURL(string: path)!) // Set the method to POST request.HTTPMethod = "POST" do { // Set the POST body for the request let jsonBody = try NSJSONSerialization.dataWithJSONObject(body, options: .PrettyPrinted) request.HTTPBody = jsonBody let session = NSURLSession.sharedSession() let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in if let jsonData = data { let json:JSON = JSON(data: jsonData) onCompletion(json, nil) } else { onCompletion(nil, error) } }) task.resume() } catch { // Create your personal error onCompletion(nil, nil) } } }
As we only want to expose the functions we need, we make our GET and POST request methods private.
We also define a typealias for all of our responses, which will always consist of the JSON object and an optional NSError.
There is nothing really special in here, we just make plain HTTP calls and can pass the data object directly into the initializer of SwiftyJSON.
If you have any questions to the implementation of the REST Manager, please leave a comment below!
Parse JSON with Swift
Now that we got the backbone of our app, we take care of the View. But before we actually craft the final view, we create a wrapper for our REST response. To parse JSON with Swift we could only rely on SwiftyJSON, which makes life already a lot easier.
To get to a stage of even better understandable code I recommend to create wrappers for your responses. By doing this you only have to access values by their name once in the init of the object. This means, if anything changes from the backend side, you only have to change it in one location in your app.
This can save a big amount of time in big projects, plus using the objects from then on is a lot friendly for everyone reading and working with your code.
So enough words, create a UserObject Swift file and insert:
import SwiftyJSON class UserObject { var pictureURL: String! var username: String! required init(json: JSON) { pictureURL = json["picture"]["medium"].stringValue username = json["email"].stringValue } }
The values we access can be seen in the response of the Randomuser API, for now we just use the name and an image. Let’s see how to create our user objects from our controller.
UPDATE: We need to use the “email” field now as the “name” field seems to be empty.
Inside your project you should already have a quite empty ViewController, so we can use that one for our tableView.
We want to display all the users we receive from the API inside a simple UITableView. Additionaly we want to have a little button to reload more users into our list. A POST request would have no effect on the Randomuser API side, so we just focus on get and creating our objects now.
Open your ViewController and replace everything inside with:
import UIKit import SwiftyJSON class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { var tableView: UITableView! var items = [UserObject]() override func viewWillAppear(animated: Bool) { let frame:CGRect = CGRect(x: 0, y: 100, width: self.view.frame.width, height: self.view.frame.height-100) self.tableView = UITableView(frame: frame) self.tableView.dataSource = self self.tableView.delegate = self self.view.addSubview(self.tableView) let btn = UIButton(frame: CGRect(x: 0, y: 25, width: self.view.frame.width, height: 50)) btn.backgroundColor = UIColor.cyanColor() btn.setTitle("Add new Dummy", forState: UIControlState.Normal) btn.addTarget(self, action: "addDummyData", forControlEvents: UIControlEvents.TouchUpInside) self.view.addSubview(btn) } func addDummyData() { RestApiManager.sharedInstance.getRandomUser { (json: JSON) in if let results = json["results"].array { for entry in results { self.items.append(UserObject(json: entry)) } dispatch_async(dispatch_get_main_queue(),{ self.tableView.reloadData() }) } } } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.items.count; } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("CELL") if cell == nil { cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL") } let user = self.items[indexPath.row] if let url = NSURL(string: user.pictureURL) { if let data = NSData(contentsOfURL: url) { cell?.imageView?.image = UIImage(data: data) } } cell!.textLabel?.text = user.username return cell! } }
As you can see, a very straight forward UITableView with an items
array for our users and a UIButton to call addDummyData
.
Inside this function we call the singleton instance of our RestApiManager
an use the JSON result as an array from which we create a new UserObject
for each entry.
In only a few lines we have added a lot of logic and parsing and can now use the Object in the standard Swift way!
That’s what I really like about doing it this way. If you look at the cellForRowAtIndexPath
function, you see how easy we can access the name and image of a user now, plus some if let
logic to only load data into an image that really exists.
Now go ahead an run your app!
UPDATE: If you encounter “Transport Security Has Blocked a cleartext http….” which you will for sure, you should edit your .plist file and add App Transport Security Settings and under App Transport Security Settings set Allow Arbitrary Loads to Yes.
See how in this Stackoverflow post.
Thanks again to Jasper for reporting this!
Conclusion
JSON parsing with Swift can be quite easy using the right approach. In general you don’t want to access the variables inside the JSON often by using the string names, that’s why it’s a good approach to seperate this logic into wrapper classes or structs you can use after creating the initial object from JSON!
If you want to see some real JSON parsing in action, check out my online course Swift in Action: Building Realtime iOS Apps with Firebase!
Happy Coding,
Simon
The post How to call REST API and Parse JSON with Swift 2.2 appeared first on Devdactic.