Getting to know Alamofire, SwiftJSON - iOS Swift in Xcode 11

Getting to know Alamofire, SwiftJSON - iOS Swift in Xcode 11

If you are a beginner or intermediate iOS developer, dealing with Network calls and handling JSON data can be a daunting task. Thanks to Alamofire Software Foundation and SwiftyJSON for these two awesome libraries.

Alamofire is a library to elegantly handle HTTP connections, and SwiftyJSON is a library to elegantly serialize JSON into Swift objects. You don't need SwiftyJSON to serialize JSON in swift, but it can get ugly having to deal with optionals.

In this tutorial we are going to build a small iOS Contacts App, using a contacts API from our friends at Android Hive https://api.androidhive.info/contacts/.

What we are going to LEARN: How to use Alamofire with SwiftyJSON to hit an API and fetch  results, parse them, and show the results in an UITableViewController. We will also learn how to segue to next view controller and setting variables from previous view controller.

Without further ado, let's dive into it: Lets create a new single view app XCode Project and install CocoaPods or Pods(these words are used interchangeably): To install Pods in your project, navigate to your root project directory using terminal and run the following commands in order.

pod init
Once you have initialized the Podfile, you need to open the file using the following command
open Podfile
This command will open the Podfile
 pod 'SwiftyJSON'
 pod 'Alamofire', '~> 4.5'
Add these pods on the podfile, right below # Pods for [Your Project Name]
pod install
This will install pod into your XCode Project
Your Podfile will look like this

Once you have managed to add Alamofire and SwiftyJSON pods into your project, you may close the current project XCode environment and open it using the newly created workspace file in the project folder.

Open the highlighted project workspace file.Note : From now you have to use the .xcworkspace to open your project.

Lets go to the main storyboard file and add Navigation Controller onto our main viewcontroller called contactsViewController. We will also add a table view on the contactsViewController storyboard file and add zero constraints on the contactsTableView for it to fill the whole contactsViewController.

At the point I assume you have created two storyboards view controllers, one that houses the contacts in a table view and name it contactsViewController and the other one that houses the contacts details which we will name contacts Details ViewController as depicted in the image above. Let's get out hands dirty with code, lets create Utils.Swift file that houses our base url and contacts endpoint and also a completion handler.

import Foundation


let API_BASEURL = "http://api.androidhive.info/"

let API_CONTACTS = API_BASEURL+"contacts/"

typealias DownloadComplete = () ->()
Utils.swift

Let's create our contacts model, I have named it Contacts.swift, this section is opinionated. You would notice that I have imported SwiftyJSON into contacts model and created a class that houses contacts private variables and initialisation method.

import Foundation
import SwiftyJSON

class Contacts {
    
    private var _id:String!
    private var _name: String!
    private var _email: String!
    private var _address: String!
    private var _gender:String!
    private var _mobile:String!
    
    
    var id:String {
        if _id == nil {
            _id = ""
        }
        
        return _id
    }
    
    var name:String! {
        if _name == nil {
            _name = ""
        }
        
        return _name
    }
    
    var email:String {
        if _email == nil {
            _email = ""
        }
        
        return _email
    }
    var address:String {
        if _address == nil {
            _address = ""
        }
        
        return _address
    }
    
    var gender:String {
        if _gender == nil {
            _gender = ""
        }
        
        return _gender
    }
    
    var mobile:String{
        if _mobile == nil{
            _mobile = ""
        }
        
        return _mobile
    }
    
    init(contactDict: JSON) {
        self._id = contactDict["id"].stringValue
        self._name = contactDict["name"].stringValue
        self._address = contactDict["address"].stringValue
        self._email = contactDict["email"].stringValue
        self._mobile = contactDict["phone"]["mobile"].stringValue
        self._gender = contactDict["gender"].string
    }
    
}
Contacts.swift

Let's go on and create the ContactsViewController.swift and create a contacts array of the type Contacts Model, We will append contacts data coming from API into this array and use it to populate our table view stubs.

Let's jump straight and talk about the ContactsAPICalling function a little bit, You can notice that this function has a completion handler that will help us to know when the contacts data has been full downloaded so that we can reload the tableview.

We then fired a GET request to our contacts endpoint without headers and got the responseJson as contactsResponse and then assigned the response result into the result variable. The line below shows how to grab all contacts data from the "contacts" array of the contacts endpoint.

let contactsJSON = JSON(result.value!)["contacts"]
Android Hive Contacts API Postman Snapshot
import UIKit
import Alamofire
import SwiftyJSON

class ContactsViewController: UIViewController {
    
    var contacts = [Contacts]()
    
    @IBOutlet weak var contactsTableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        contactsTableView.dataSource = self
        contactsTableView.delegate = self
        
        
        self.ContactsAPICalling {
            print("Contacts Downloaded")
            self.contactsTableView.reloadData()
        }
    }
    
    func ContactsAPICalling(completed: @escaping DownloadComplete){
        
        Alamofire.request(API_CONTACTS, method: .get, encoding:   JSONEncoding.default, headers: nil).responseJSON { (contactsResponse) in
            
            let result = contactsResponse.result
            let contactsJSON = JSON(result.value!)["contacts"]
            
            print(contactsJSON)
            
            for i in 0..<contactsJSON.count {
                
                let allContacts = Contacts(contactDict: contactsJSON[i])
                
                self.contacts.append(allContacts)
            }
            
            completed()
       
        }
        
    }

}
ContactsViewController.Swift

Let's create an extension of ContactViewController to handle the contacts data and interaction of the table view through UITableViewDataSource and UITableViewDelegate. The "contacts.count" will populate the number of contacts on the number of rows in section table view stub.

We will then use CellForRowAt table view function to registers our table view cell, you will notice that we did not create a custom cell rather we used the inbuilt UITableViewCell style subtitle with the reusable identifier "contactscell". From this point it becomes easier to populate our cell two labels as seen below.

On the didSelectRowAt, we will then perform a segue to navigate to our contactsDetailsViewController through a segue identifier "toContactsView". From this point we will need pass the contacts variable onto our destination View Controller to able to populate the selected row contact details on the next view controller.

extension ContactsViewController: UITableViewDataSource,UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
       return contacts.count
    }
    
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
 contactsTableView.register(UITableViewCell.self, forCellReuseIdentifier: "contactscell")
        
 var cell = contactsTableView.dequeueReusableCell(withIdentifier: "contactscell")
        
        if cell == nil {
            cell = UITableViewCell(style: .subtitle, reuseIdentifier: "contactscell")
        }
        
        cell?.textLabel?.text = contacts[indexPath.row].name
        cell?.detailTextLabel?.text = contacts[indexPath.row].email
        
        return cell!
      
    }
    
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:   IndexPath) {
        performSegue(withIdentifier: "toContactsView", sender: self)
    }
    
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destinationVC = segue.destination as? ContactDetailsViewController {
            
destinationVC.contactDetails = contacts[(contactsTableView.indexPathForSelectedRow?.row)!]
            contactsTableView.deselectRow(at: contactsTableView.indexPathForSelectedRow!, animated: true)
            
        }
    }
 
}
ContactsViewController.Swift

Let's finish up the last part of the projects by segueing to ContactDetailsViewController which houses contact details IBOutlets. We will use the contactDetails variable to populate the contact outlets in viewDidLoad function.

import UIKit

class ContactDetailsViewController: UIViewController {
    
    var contactDetails:Contacts!
    
    @IBOutlet weak var FullNameLbl: UILabel!
    @IBOutlet weak var MobileNumberLbl: UILabel!
    @IBOutlet weak var AddressLbl: UILabel!
    @IBOutlet weak var EmailLbl: UILabel!
    @IBOutlet weak var GenderLbl: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        FullNameLbl.text = contactDetails.name
        MobileNumberLbl.text = contactDetails.mobile
        AddressLbl.text = contactDetails.address
        EmailLbl.text = contactDetails.email
        GenderLbl.text = contactDetails.gender
    }

}
ContactDetailsViewController.Swift

Here is the Github Repository Link to the project https://github.com/TheDoer/Alamofire-SwiftyJSON-Tutorial