Sunday, June 22, 2014

Access REST Web Service with Apple’s new Swift Language - Part 2

In my last post, I showed how to use Apple’s new Swift programming language to access Apple’s Restful iTunes Search API to retrieve a list of songs by Jimmy Buffett.  In the post we simply logged the JSON results to the console.  In this post we will expand on that example to display the results, with album covers, in a UITableView.  We have two requirements for loading images, they must be loaded in the background so the UI does not freeze while the images load and to create an image cache so we do not reload images we already have.

If you have not gone though my last post, you will want to get the code at the end of that post because we will be using the same ITunesSearchAPI class here.

Lets start off by adding the UITableViewDataSource and the UTTableViewDelegate to our ViewController.  The ViewController definition will look like this:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, ITunesSearchAPIProtocol
{

}

Now lets define the IBOutlet for our UITableView, the NSArray to hold our table data, the NSMutableDictionary that will be our image cache and the ITunesSearchAPI object (the code for the ITunesSearchAPI can be found in my last post).  Add the following code to the ViewController class:

    @IBOutlet var appsTableView : UITableView
    var api: ITunesSearchAPI = ITunesSearchAPI()
    var tableData: NSArray = NSArray()
    var imageCache = NSMutableDictionary()

 Lets save the ViewController class and go to the Storyboard.  In the view for the ViewController add a UITableView and then set the dataSource and delegate like you did with Objective-C.




Now lets go back to the ViewController class.  There are no changes to the viewDidLoad() function from my previous post.  The code should look like this:

    override func viewDidLoad() {
        super.viewDidLoad()       
        api.delegate = self;
        api.searchItunesFor("Jimmy Buffett")
    }

We need to make a change to the didReceiveResponse function so our tableview reloads once the data is received from the iTunes search API.  The new didReceiveResponse looks like this:

    func didRecieveResponse(results: NSDictionary) {
        // Store the results in our table data array
        println("Received results")
        if results.count>0 {
            self.tableData = results["results"] as NSArray
            self.appsTableView.reloadData()
        }

    }

Lets implement the delegate functions for the UITableView.  For this example we will be implementing the following three functions:

·       func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
·       func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
·       func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)

If you are familiar with using a UITableView in Objective-C, then these functions should look very familiar.  The first function returns the number of rows for the table view.  The second function returns a UITableViewCell to render for the given row.  The third function is called when a row is selected.  Now lets implement these functions.

The first UITableView delegate function simply returns the number of rows for the table view so this function will return the number of objects in our tableData NSArray object.

    func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return tableData.count
    }

The second UITableView delegate function is a bit more complicated.  Lets look at the code first and then I will walk though it.

    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath:
                NSIndexPath!) -> UITableViewCell! {
        let kCellIdentifier: String = "MyCell"
       
        //the tablecell is optional to see if we can reuse cell
        var cell : UITableViewCell?
        cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as?
               UITableViewCell
       
        //If we did not get a reuseable cell, then create a new one
        if !cell? {
            cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:
                     kCellIdentifier)
        }

        //Get our data row
        var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary
       
        //Set the track name
        let cellText: String? = rowData["trackName"] as? String
        cell!.text = cellText
        // Get the track censored name
        var trackCensorName: NSString = rowData["trackCensoredName"] as NSString
        cell!.detailTextLabel.text = trackCensorName

        cell!.image = UIImage(named: "loading")
     
       
        dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
           
            // Grab the artworkUrl60 key to get an image URL
            var urlString: NSString = rowData["artworkUrl60"] as NSString
           
            // Check the image cache for the key (using the image URL as key)
            var image: UIImage? = self.imageCache.valueForKey(urlString) as? UIImage
           
            if( !image? ) {
                // If the image does not exist in the cache then we need to download it
                var imgURL: NSURL = NSURL(string: urlString)
               
                //Get the image from the URL
                var request: NSURLRequest = NSURLRequest(URL: imgURL)
                var urlConnection: NSURLConnection = NSURLConnection(request: request,
                         delegate: self)

                NSURLConnection.sendAsynchronousRequest(request, queue:
                          NSOperationQueue.mainQueue(), completionHandler: {(response:
                          NSURLResponse!,data: NSData!,error: NSError!) -> Void in

                    if !error? {
                        image = UIImage(data: data)
                       
                        // Store the image in the cache
                        self.imageCache.setValue(image, forKey: urlString)
                        cell!.image = image
                        tableView.reloadData()
                    }
                    else {
                        println("Error: \(error.localizedDescription)")
                    }
                    })
               
            }
            else {
                cell!.image = image
            }
           
           
            })
       
       
        return cell
       
    }

We start this function by getting a reusable cell.  If we are unable to reuse a cell we create a new one.  This is pretty standard for a UITableView because reusing cells cuts down on memory usage when we have table views that contain large number of rows.

After we have our cell, we retrieve the data from the tableData NSArray object.  We do this by getting the row from the NSIndexPath object that was passed into this function.  Once we have the data we are able to populate the cell.   We retrieve the track name and also the track censor name and display them in the cell’s text and detailTextLabel properties.

We set the cell’s image object to an image named loading.png.   This image is a temporary image that shows the image is loading in the background.  This is what the image looks like (empty box with the word "Loading"):



We now want to load the image of the album cover in the background so our UI does not freeze.  We do this by using the dispatch_async function from GCD to submit a block for asynchronous execution on a dispatch queue.  This block contains the code to retrieve the image from iTunes search API.

We begin the block by getting the URL for the album image.  We then try to retrieve the image from our image cache.  The line: if (!image ?)  Reads “if the image is nil”.  If the image is nil, we need to download it from the iTunes search API.

To download the image from the iTunes search API, we first create an NSURL object and use it to create an NSURLRequest.  We then use the NSURLRequest to create an NSURLConnection object and call the sendAsynchronousRequest function to download the image.

In the completion handler of the of the sendAsynchronousRequest function, we pass a block of code that handles the image when it is received.  This block begins by checking for any errors.  If there were no errors, we create an UIImage object from the data that was received, store it in the image cache and add it to the cell.

The third UITableView delegate function will display a UIAlertView with the track’s censored name and the release date of the track that was selected.  Let’s look at the code for this function.

    func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
          //get the selected tracks information
        var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary

        var name: String = rowData["trackCensoredName"] as String
        var releaseDate: String = rowData["releaseDate"] as String
       
          //Show the alert view with the tracks information
        var alert: UIAlertView = UIAlertView()
        alert.title = name
        alert.message = releaseDate
        alert.addButtonWithTitle("Ok")
        alert.show()
    }

This function retrieves the track information and then pops up a UIAlertView displaying the track’s censored name and the release date.


If you have suggestions about other topics that you would like to see covered here, please leave a comment. 

Monday, June 9, 2014

Access REST Web Service with Apple’s new Swift Language

In Apple’s own words, “Swift is an innovative new programming language for Cocoa and Cocoa Touch”.  From what I have seen so far I would agree with Apple’s description.  In this post I will show how easy it is to access a REST based Web Service with Swift by writing a simple iTunes search API.

So lets get started.  You will need XCode beta 6 to work with Swift so if you have not already downloaded/installed it you will need to do so now.   Once you have XCode 6 beta on your machine, start a new project and select the Single View Application template.


On the next option screen, enter the name of your project and then select Swift as the language.



Finally select the location of your project and now you are ready to start. 

We will begin by creating a ITunesSearchAPI class that will be used to make requests to Apple’s iTunes search API.  In this class we will define a Protocol that will be used to return the results of our search.  Lets start by defining our protocol:

protocol ITunesSearchAPIProtocol {
    func didRecieveResponse(results: NSDictionary)
}

The ITunesSearchAPIProtocol contains one function.  This function will receive an NSDictionary object that contains the results of our request to iTunes.

Now lets start the ITunesSearchAPI class.  We will start off by defining two properties like this

class ITunesSearchAPI: NSObject {
    var data: NSMutableData = NSMutableData()
    var delegate: ITunesSearchAPIProtocol?
}

These properties are the data property used to store the data coming back from the server and the delegate property that is used to define our ITunesSearchAPIProtocol.  Now lets create a searchItunesFor function that we will call to perform our search (Note this code has been updated for Swift 1.2, I have noted where changes were made form the original code).

func searchItunesFor(searchTerm: String) {
        
        //Clean up the search terms by replacing spaces with +
        var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ",withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch,range: nil)
        
        // Changed for Swift 1.2
        // var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
        if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
            
            var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music"
            
            // Changed with Swift 1.2
            //  var url: NSURL = NSURL(string: urlPath)
            if let url = NSURL(string: urlPath) {
                var request: NSURLRequest = NSURLRequest(URL: url)
                
                //changed with Swift 1.2
                //  var connection: NSURLConnection = NSURLConnection(request: request,delegate: self,startImmediately: false)
                if let connection = NSURLConnection(request: request,delegate: self,startImmediately: false) {
                    
                    println("Search iTunes API at URL \(url)")
                    
                    connection.start()
                }
            }
        }

    }

The first two lines clean up the search term string that was passed into the searchItunesFor function so it can be used to create our search URL.  We then use this URL to create a NSURL object.  That NSURL object is used to create an NSURLRequest object and finally the NSURLRequest is used to create a NSURLConnection object.  Notice we set the startImmediately parameter, of the NSURLConnection, to false, this prevents the request from starting immediately.  If there is no additional code needed, you can set this to True to make the request run immediately but I like setting it to false and manually starting the connection.  This allows me to initialize anything needed prior to the request being made.  The last line is what manually starts the NSURLConnection.

Now lets create the NSURLConnectionDataDelegate functions.  Here is the first function which is called if the connection fails:

    //NSURLConnection Connection failed
    func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
        println("Failed with error:\(error.localizedDescription)")
    }

This next function is called when a new connection is established.  In this function we simply clear the data property.

    //New request so we need to clear the data object
    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response:NSURLResponse!) {
        self.data = NSMutableData()
    }
   
This function is called each time data is received from the server.  In this function we append the new data to our data property.

    //Append incoming data
    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
         self.data.appendData(data)
    }

This last function is called after all information is received from the server.  In this function we convert the data received from the server to a NSDictionary object.  We then call the delegate’s didReceiveResponse function with the NSDictionay object.  (Note this code has been updated for Swift 1.2, I have noted where changes were made form the original code)
   
    //NSURLConnection delegate function
    func connectionDidFinishLoading(connection: NSURLConnection!) {
        //Finished receiving data and convert it to a JSON object

        //Changed with Swift 1.2
        // var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,options:NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
        
        delegate?.didRecieveResponse(jsonResult)
    }


Now lets look at how we would use the ITunesSearchAPI class.  The first thing we need to do is to add the ITunesSearchAPIProtocol to the list of protocols that the ViewController adopts.  To do this, simply add the ITunesSearchAPIProtocol to the comma-separated list following the name of the class’s super class like this: 

class ViewController: UIViewController, ITunesSearchAPIProtocol

In the apps ViewController.swift file add one property like this:

var api: ITunesSearchAPI = ITunesSearchAPI()

This will set the api property to our ITunesSearchAPI type and initiate it.   We then want to add the following two lines to the viewDidLoad function.

api.delegate = self;
api.searchItunesFor("Jimmy Buffett")

These two lines sets the delegate of our api property to this object and then calls the searchItunesFor function passing it the string “Jimmy Buffett” as the search term. 

Finally add our delegate function like this:

func didRecieveResponse(results: NSDictionary) {
    println(results)
}

This function simply logs the response to the console.  In future posts we will expand this function to actually do something with the data.

The full ITunesSearchAPI.swift file should look like this:


import UIKit

protocol ITunesSearchAPIProtocol {
    func didRecieveResponse(results: NSDictionary)
}

class ITunesSearchAPI: NSObject {
    var data: NSMutableData = NSMutableData()
    var delegate: ITunesSearchAPIProtocol?
   
    //Search iTunes
    func searchItunesFor(searchTerm: String) {
       
        //Clean up the search terms by replacing spaces with +
        var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+",
                          options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
       
        var escapedSearchTerm =                         itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
        var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music"
        var url: NSURL = NSURL(string: urlPath)
        var request: NSURLRequest = NSURLRequest(URL: url)
        var connection: NSURLConnection = NSURLConnection(request: request, delegate: self,
                          startImmediately: false)
       
        println("Search iTunes API at URL \(url)")
       
        connection.start()
    }
   
    //NSURLConnection delegate method
    func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
        println("Failed with error:\(error.localizedDescription)")
    }

    //NSURLConnection delegate method
    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        //New request so we need to clear the data object
        self.data = NSMutableData()
    }
   
    //NSURLConnection delegate method
    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
        //Append incoming data
        self.data.appendData(data)
    }
   
    //NSURLConnection delegate method
    func connectionDidFinishLoading(connection: NSURLConnection!) {
        //Finished receiving data and convert it to a JSON object
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,
                          options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary

        delegate?.didRecieveResponse(jsonResult)
    }

}


The full ViewController.swift file should look like this:

import UIKit

class ViewController: UIViewController, ITunesSearchAPIProtocol{
   
    var api: ITunesSearchAPI = ITunesSearchAPI()
   
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
       
        api.delegate = self;
        api.searchItunesFor("Jimmy Buffett")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func didRecieveResponse(results: NSDictionary) {
        // Store the results in our table data array
        println(results)
    }

}


Part 2:  In part 2 we will expand on the code presented here to display the results, with album covers, in a UITableView.  We will have two requirements for loading images, they must be loaded in the background so the UI does not freeze while the images load and to create an image cache so we do not reload images we already have. To read part 2, follow this link Access REST Web Service with Apple’s new Swift Language - Part 2