Alamofire Sweet Asynchronous Networking

Modern programming in iOS implies many rules to enhance the app responsiveness and throughput. One of this is multitasking and asynchronous tasks.

Asynchronous programming results in delegating consuming time tasks to a second thread to keep the app responsive while the task is performing in the background.

One example of asynchronous programming can be shown in the App Store app, after you search for an app, the list of app images and screenshots is loaded asynchronously as you scroll down the list.

In this tutorial, I gonna show you how to implement such pattern in your app, you will communicate with the iTunes search API to look for apps and games from the iTunes store and display the list of results in a UITableView with a background image loading so that your app will keep its responsiveness along the way.

Here is what the final project will look like:

Asynchronous image loading

Some parts of this tutorial are not related to networking, so I have prepared a starter project containing all of this work ready to go for you in order to better focus on the main topic.

So, to begin, download the starter project here.

Open the project and select the Project navigator view.

Project navigator view

Let me explain the main groups and files:

SDWebImage: This library provides a UIImageView category for a better image asynchronous loading
iTunesAppBrowser-Bridging-Header.h: The first time you import Objective-C files into your Swift project, Xcode will ask to create a bridging file in which you will expose all Objective-C code to Swift. This is important since it allows to use the SDWebImage library (which is still written in Objective-C)from within our swift class.
Alamofire: This is the networking library which will make the request/response tasks easy to do. Alamofire is built on NSURLSession class and related classes, which means that the app still benefit from NSURLSession powerful download API.
iTunesCell.swift: A UITableViewCell subclass to hook up the cell objects (button, labels, image view). This class is important because it allows to get a reference of each cell generated programmatically in runtime.

Talking about the latter class, let’s go ahead and hook up the cell objects to it, select Main.storyboard from the Project navigator view and switch to the Assistant editor. Make sure that iTunesCell.swift file is open in the editor view along with the storyboard file.

Next, ctrl+drag from the order number label to the source file, just below the class name, make sure it’s an outlet connection, name it ‘appOrder’ and click the Connect button.

Now repeat the same step with the rest of objects (the image view, the two labels and the button on the right), ctrl+drag from, one by one to the source file as Outlet connections and name them ‘appArtwork’, ‘appName’, ‘sellerName’ and ‘getAppButton’ respectively.

Locate the function named ‘awakeFromNib’, and add the following statements inside (just after the super.awakeFromNib call) in order to customise the image view corners radius :

appArtwork.layer.cornerRadius = 5
appArtwork.layer.masksToBounds = true

Here is the final iTunesCell.swift file:

import UIKit

class iTunesCell: UITableViewCell {

    @IBOutlet var appOrder: UILabel!
    @IBOutlet var appArtwork: UIImageView!
    @IBOutlet var getAppButton: UIButton!
    @IBOutlet var sellerName: UILabel!
    @IBOutlet var appName: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        appArtwork.layer.cornerRadius = 5
        appArtwork.layer.masksToBounds = true
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    }
}

That’s it for the cell setup, now time to implement the view controller to manage all networking stuff.

Select ViewController.swift from the Project navigator view and declare the following two variables just below the class name:

var appArray:NSArray = NSArray()
var appDictionary:NSDictionary!

The iTunes API will return a JSON array of dictionaries, appArray will hold it and will work as the table view data source, while appDictionary will handle each dictionary of the array a part, to extract its data in the cellForRowAtIndexPath protocol method.

The table view will embed a search bar at the top to allow for manual search queries, so let’s implement the search bar at the header of the table. Place the following code inside viewDidLoad method (just below the super.viewDidLoad call):

 var searchBar:UISearchBar = UISearchBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 44))
 searchBar.searchBarStyle = UISearchBarStyle.Minimal
 searchBar.delegate = self
 searchBar.placeholder = "Search for apps, games, etc"
 self.tableView.tableHeaderView = searchBar

The code above will programmatically draw a search bar to fill the whole screen width and set it as the header view of the table to remain at the top of the content.

Let’s adopt the class to the UISearchBarDelegate protocol, update the class name to the following:

class ViewController: UITableViewController, UISearchBarDelegate{

Next, place the following data source protocol methods implementation before the closing bracket of the view controller:

override func tableView(tableView: UITableView,
        cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
            //1
            var cell:iTunesCell = self.tableView.dequeueReusableCellWithIdentifier("iTunesAppCell") as! iTunesCell
            appDictionary = self.appArray.objectAtIndex(indexPath.row) as! NSDictionary
            cell.appOrder.text     = String(indexPath.row+1)
            cell.appName.text      = appDictionary.valueForKey("trackName") as? String
            cell.sellerName.text   = appDictionary.valueForKey("sellerName") as? String
            cell.getAppButton.tag = indexPath.row
            cell.getAppButton.addTarget(self, action: "openAppInAppStore:", forControlEvents: UIControlEvents.TouchUpInside)
            //2
            let block: SDWebImageCompletionBlock! = {(image: UIImage!, error: NSError!, cacheType: SDImageCacheType, imageURL: NSURL!) -> Void in
            }
            let url:NSURL = NSURL(string: appDictionary.valueForKey("artworkUrl100") as! String)!
            cell.appArtwork.sd_setImageWithURL(url, placeholderImage:UIImage(named: "placeholder.png"),completed:block)
            
            return cell
    }
    // Assign the number of rows for the table view
    override func tableView(tableView: UITableView,
        numberOfRowsInSection section: Int) -> Int{
            return self.appArray.count
}

Some parts of the above code are worth explanation:

//1: We used the dequeueReusableCellWithIdentifier method to return a cell instance from the storyboard custom cell, if there is no recycled cell to be reused, this method will create a new cell using the informations in the storyboard. That’s why, the cell identifier in the storyboard is matching the one used in the code (in this case ‘iTunesAppCell’). Instantiating cells this way will eliminate the need to check the return value of the dequeueReusableCellWithIdentifier method for nil, and will save us time creating cells manually 🙂

The rest of the code is just some cell configuration from the appDictionary to fill in the data of the app record.

//2: This is another swift powerful feature, you assigned the SDWebImage completion block to a constant and pass it to the setImageWithURL API to asynchronously download the image from the iTunes server.

So far so good, run the app and type in an app name, nothing will show up, let’s implement one more method to fire the search process to the iTunes API and display the data on the table view.

Place the following UISearchBarDelegate protocol method before the class closing bracket:

  func searchBarSearchButtonClicked(searchBar: UISearchBar){
        searchBar.resignFirstResponder()

        let manager = Manager.sharedInstance
        manager.request(.GET, "https://itunes.apple.com/search", parameters: ["term":searchBar.text,"country":"us","entity":"software"])
            .responseJSON(options: NSJSONReadingOptions.AllowFragments) { (_, _, jsonData, error) -> Void in

                self.appArray = jsonData?.objectForKey("results") as! NSArray
                if self.appArray.count > 0{
                    self.tableView.reloadData()
                }else{
                    let alert:UIAlertView = UIAlertView(title: nil, message: "No apps found", delegate: self, cancelButtonTitle: "Try again")
                    alert.show()
                }
        }
    }

The code above uses a shared instance of the NSURLSessionConfiguration class, it instantiates a defaultSessionConfiguration object to launch a request to the iTunes API, the response is then parsed and reloaded into the table view.

Let’s finish up with an additional function to open the selected app into the App Store when a user clicks on the right button of the cell. Place the following code to accomplish this:

    func openAppInAppStore(sender: UIButton){
        let dic = self.appArray.objectAtIndex(sender.tag) as! NSDictionary
        let appURL:NSURL = NSURL(string: dic.valueForKey("trackViewUrl")as! String)!
        UIApplication.sharedApplication().openURL(appURL)
    }

That’s it, run the project, search for your app and enjoy the asynchronous loading 🙂

As usual, you can download the final project here.

What tips and libraries you use for asynchronous networking in your app? I’d love to hear your thoughts, feel free to leave a comment below!

Malek
iOS developer with over than 11 years of extensive experience working on several projects with different sized startups and corporates.

1 Comment

Comments are closed.