Sunday, February 8, 2015

Mastering Swift

A couple months back the publisher of my first book, Packt Publishing, asked me if I would be interested in writing a book on the Swift programming language.  I quickly agreed and went to working writing it.  This book will be titled Mastering Swift.

While most books on Swift programming focus on developing applications for iOS devices, this book is going to be a bit different.  We are going to focus on teaching the Swift language itself.  This means that all 350+ pages will be dedicated to helping you learn how to effectively write Swift code no matter what type of application you are writing or platform you are targeting.  

If you are a developer that learns best by looking at and working with code, then this book will be for you.  It starts off by giving the basics of Swift and slowly progresses to more advice topics like concurrency, Objective-C interoperability, networking and design patterns. 

Over the past couple of months I have submitted the first drafts for the first ten chapters and will begin work on Chapter 11 next week.  As I continue to work on this book I will provide updates on this blog and on my twitter account.  I hope that those of you that are interesting in learning Swift will continue to look for updates.  As the book gets closer to publication, I hope to provide additional details about it.

Click this link to visit Packt Publishing page to preorder the book and to also get more details about the book.  I must say that I am really excited about this book.  Granted I am a bit bias since I am the author, but I believe this book will be one of the premiere books for learning the Swift programming language.  We are going to cover a lot of details that most other books are unable to cover because they are focusing on how to write applications for a specific platform while this book will be all about the Swift language itself.  


While I believe I can write a great book on the Swift programming language, what really has me excited about the book and why I believe this book will be special is the incredibly awesome team that Packt Publishing has to assist me with it.  So if you are a developer that wants to learn the Swift programming language or a Swift developer that wants to take your skills to the next level check out my Mastering Swift book, it may be the book you have been searching for. 

Tuesday, August 26, 2014

New Release of RSNetworking – a networking library for Swift

RSNetworking is a network library written entirely in the Swift programming language.  RSNetworking is built using Apple’s powerful URL Loading System.  The main design goal of RSNetworking is to make it easy and quick for developers to add powerful asynchronous networking requests, to their applications written in Swift.

Here are the changes in this release of RSNetworking:
  • Removed the RSNetworking class:  This class was marked for depreciation two releases ago and was removed this release.
  • Updated the UIImageView+RSNetworking extension:  This extension was still using RSNetworking.  Updated it to start using RSURLRequest.
  • Added the UIButton+RSNetworking extension:  This extension gives you the ability to asynchronously load images from the Internet.  Once the image has downloaded it will be used as the background image for the button. 


You can get RSNetworking and see examples on GitHub.  Please let me know if you would like to contribute to RSNetworking or if you have any suggestions.

Wednesday, July 30, 2014

Using Swift with RSNetworking to access Apple's iTunes search API

RSNetworking is a networking library that I am writing in the Swift programming language.  I have written a couple posts that show the features of RSNetworking and how to use them but I have not shown how to use RSNetworking in a complete project.  This post will walk you though creating an app that will search iTunes using Apple's search API and display the results in a UITableView using the new RSNetworking library.  The GitHub repository for RSNetworking is located here:  https://github.com/hoffmanjon/RSNetworking

Lets begin by creating a Single-View application project, using Swift as the programming language, with XCode Beta 4.  Once the project is created, copy the RSNetworking files to the project.  This will include the follow files:

RSTransaction.swift
RSTransactionRequest.swift
RSURLRequest.swift
RSUtilities.swift
UIImageView+RSNetworking.swift

Notice that I did not include the RSNetworking.swift file.  This file will be removed from the library in the very near future.

Now lets add a UITextField, UIButton and a UITableView to the View Controller Scene.  The scene should look like this in the storyboard.



Now lets go to our ViewController.swift file.  The first thing we need to do is to add some outlets that will be used to connect our code to the UI elements.  We will need three outlets one each for the UITextField, UIButton and UITableView.  We define the outlets like this (you can see the complete ViewController.swift class at the end of this post):

@IBOutlet var searchButton : UIButton!
@IBOutlet var searchTextField : UITextField!
@IBOutlet var resultsTableView : UITableView!
   
After these are added to the class, we will need to connect them to the UI elements in the storyboard.  Next we will need to define an NSArray that will hold the data for our UITableView.  We will be using an NSArray instead of a Swift Array because the array will contain various objects like a Strings, Arrays or Dictionaries.   We would need to define the Swift array like this [Dictionary<String, Object>] = [] but this is not allowed in Swift.  In Swift you need to define the type for both the key and value in a dictionary.  The NSArray is defined like this:

var tableData: NSArray = NSArray()


We also want to initialize an instance of the RSTransationRequest class that will be used to make our requests.  The RSTransationRequest class is used to make the requests to the server and return the results in the format we request.  We initialize it like this:

var rsRequest: RSTransactionRequest = RSTransactionRequest()

The RSTransactionRequest exposes four functions.  These are:
* dataFromRSTransaction(transaction: RSTransaction, completionHandler handler: RSNetworking.dataFromRSTransactionCompletionCompletionClosure):
 Retrieves an NSData object from the service defined by the RSTransaction.  This is the main function and is used by the other three functions to retrieve an NSData object prior to converting it to the required format. 
* stringFromRSTransaction(transaction: RSTransaction, completionHandler handler:  RSNetworking.stringFromRSTransactionCompletionCompletionClosure):  
Retrieves an NSString object from the service defined by the RSTransaction.  This function uses the dataFromRSTransaction function to retrieve an NSData object and then converts it to an NSString object. 
*dictionaryFromRSTransaction(transaction: RSTransaction, completionHandler handler:  RSNetworking.dictionaryFromRSTransactionCompletionCompletionClosure):
 Retrieves an NSDictionary object from the service defined by the RSTransaction.  This function uses the dataFromRSTransaction function to retrieve an NSData object and then converts it to an NSDictionary object.  The data returned from the URL should be in JSON format for this function to work properly. 
*imageFromRSTransaction(transaction: RSTransaction, completionHandler handler:  RSNetworking.imageFromRSTransactionCompletionCompletionClosure):
Retrieves an UIImage object from the service defined by the RSTransaction.  This function uses the dataFromRSTransaction function to retrieve an NSData object and then converts it to an UIImage object.

We also want to initialize an instance of the RSTransaction class.  The RSTransaction class is designed to contain everything needed to create a request to a HTTP web service.  We initialize it like this:

var rsTransGet: RSTransaction = RSTransaction(transactionType:  RSTransactionType.GET, baseURL: "https://itunes.apple.com", path: "search", parameters: ["term":"Jimmy+Buffett","media":"music"])

The RSTransaction class exposes four properties, one initiator and one method.  These are:
- Properties
* TransactionType - This defines the HTTP request method.  Currently there are three types, GET, POST, UNKNOWN.  Only the GET and POST actually sends a request.
* baseURL - This is the base URL to use for the request.  This will normally look something like this:  "https://itunes.apple.com".  If you are going to a non-standard port you would put that here as well.  It would look something like this:  "http://mytestserver:8080"
*path - The path that will be added to the base url.  This will normally be something like this: "search".  It can also include a longer path string like: "path/to/my/service"
* parameters - Any parameters to send to the service.
- Initiators
* init(transactionType: RSTransactionType, baseURL: String,  path: String, parameters: [String: String]) - This will initialize the RSTransaction with all properties needed.
- Function
* getFullURLString() -> String - Builds and returns the full URL needed to connect to the service.


Now we need to override the ViewController’s viewDidAppear() function.  This function is called after the view is displayed but is not part of the standard template so it is not created when XCode creates the UIViewController class.  You can override the function like this:

override func viewDidAppear(animated: Bool) {

}

Since we are writing this app to run on an iPhone/iPod/iPad we cannot guarantee that it will always be connected to the Internet.  Therefore, the first thing we need to do is to check to see if itunes.apple.com is reachable.  We can do this with the isHostnameReachable() function in the RSUtilities class.  We would add this check to the viewDidAppear function like this:

override func viewDidAppear(animated: Bool) {
    //check to see if host is reachable
    if (!RSUtilities.isHostnameReachable("www.apple.com")) {
           
        //If host is not reachable, display a UIAlertController informing the user
        var alert = UIAlertController(title: "Alert", message: "You are not conected to the Internet", preferredStyle: UIAlertControllerStyle.Alert)
           
        //Add alert action
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
           
        //Present alert
        self.presentViewController(alert, animated: true, completion: nil)
    }
}

If itunes.apple.com is not reachable, we display an alert to the user letting them know.

Now lets add the function that will be called when the search button is pressed.  It will look like this:

@IBAction func search(AnyObject) {
       
}

Don’t forget to go back into the Storyboard and connect the button to this function. 

The search() function will need to retrieve the text from the searchTextField UITextField, convert that text to a useable string (replacing whitespace with a + symbol) and then make the request to itunes.apple.com.  The full search function looks like this:

@IBAction func search(AnyObject) {
       
    //Get text for search
    var text: String = searchTextField.text;
       
    //Convert spaces to "+"
    let searchText = text.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil)
       
    //Set the parameters for the RSTransaction object
    rsTransGet.parameters = ["term":searchText,"media":"music"]
       
    //Send request
    rsRequest.dictionaryFromRSTransaction(rsTransGet, completionHandler: {(response : NSURLResponse!, responseDictionary: NSDictionary!, error: NSError!) -> Void in
       
         if !error? {
              //If there was no error
            //Set the tableData NSArray to the results that were returned from the iTunes search and reload the table
            self.tableData = responseDictionary["results"] as NSArray
             //Reload the UITableView
            self.resultsTableView.reloadData()
        } else {
            //If there was an error, log it
            println("Error : \(error)")
        }
        })
}

After we retrieve the text from the searchTextField UITextField and convert that text to a useable string, we use that new String to set the parameters of our RSTransaction object.  We then submit the RSTransaction using the dictionaryFromRSTransaction method of the RSTransactionRequest object, this will create and send the request to Apple’s iTunes search API.  

We pass a block of code to the dictionaryFromRSTransaction method that is run when a response is received.  If we did not receive an error, we populate the tableData NSArray object from the response and then reload the UITableView to display the new results.

The only thing left to do is to implement the UITableViewDelegate methods.  The first method we will implement simply returns the number of rows in the tableData NSArray:

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

The second UITableViewDelegate that we implement will create the UITableViewCells.

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
   let kCellIdentifier: String = "MyCell"
       
   //tablecell 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 the data from the NSArray
   var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary
       
   //Set the text of the cell
   cell!.textLabel.text =  rowData["trackName"] as? String
       
   //Set the detailText of the cell
   cell!.detailTextLabel.text = rowData["trackCensoredName"] as NSString
       
   //Use the setImageForURL method added to the UIImageView by the
   //RSNetworking catagory to load an image from a URL. 
   //While the image loads we use a placeholder image
   var imageURL: NSString = rowData["artworkUrl60"] as NSString
   var mCell = cell
   mCell!.imageView.setImageForURL(imageURL, placeHolder: UIImage(named: "loading"))
       
   return cell
       
}

The implementation of this method is similar to the Objective-C implementation.  The interesting part is at the end where we use the setImageForURL method that is added to the UIImageView by RSNetworking’s UIImageView+RSNetworking category.  This category will add a placeholder image that is used while the real image downloads.

I added this project to RSNetworking’s GitHub repository so you can view the project as a whole.  The name of the project is iTunesSearch. 

The complete ViewController class looks like this:

import UIKit

class ViewController: UIViewController {
   
    @IBOutlet var searchButton : UIButton!
    @IBOutlet var searchTextField : UITextField!
    @IBOutlet var resultsTableView : UITableView!
   
    var tableData: NSArray = NSArray()
    var rsRequest: RSTransactionRequest = RSTransactionRequest()
    var rsTransGet: RSTransaction = RSTransaction(transactionType: RSTransactionType.GET, baseURL: "https://itunes.apple.com", path: "search", parameters: ["term":"Jimmy+Buffett","media":"music"])
   
    override func viewDidLoad() {
        super.viewDidLoad()
    }
   
    override func viewDidAppear(animated: Bool) {
        //check to see if host is reachable
        if (!RSUtilities.isHostnameReachable("www.apple.com")) {
           
            //If host is not reachable, display a UIAlertController informing the user
            var alert = UIAlertController(title: "Alert", message: "You are not conected to the Internet", preferredStyle: UIAlertControllerStyle.Alert)
           
            //Add alert action
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
           
            //Present alert
            self.presentViewController(alert, animated: true, completion: nil)
        }
    }
   
    @IBAction func search(AnyObject) {
       
        //Get text for search
        var text: String = searchTextField.text;
       
        //Convert spaces to "+"
        let searchText = text.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil)
       
        //Set the parameters for the RSTransaction object
        rsTransGet.parameters = ["term":searchText,"media":"music"]
       
        //Send request
        rsRequest.dictionaryFromRSTransaction(rsTransGet, completionHandler: {(response : NSURLResponse!, responseDictionary: NSDictionary!, error: NSError!) -> Void in
            if !error? {
                //Set the tableData NSArray to the results that were returned from the iTunes search and reload the table
                self.tableData = responseDictionary["results"] as NSArray
                self.resultsTableView.reloadData()
            } else {
                //If there was an error, log it
                println("Error : \(error)")
            }
            })
    }
   
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
   
    //UITableView delegate methods are below:
    func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return tableData.count
    }
   
    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let kCellIdentifier: String = "MyCell"
       
        //tablecell 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 the data from the NSArray
        var rowData: NSDictionary = self.tableData[indexPath.row] as NSDictionary
        
        //Set the text of the cell
        cell!.textLabel.text =  rowData["trackName"] as? String
       
        //Set the detailText of the cell
        cell!.detailTextLabel.text = rowData["trackCensoredName"] as NSString
       
        //Use the setImageForURL method added to the UIImageView by the
        //RSNetworking catagory to load an image from a URL. 
        //While the image loads we use a placeholder image
        var imageURL: NSString = rowData["artworkUrl60"] as NSString
        var mCell = cell
        mCell!.imageView.setImageForURL(imageURL, placeHolder: UIImage(named: "loading"))
       
        return cell
       
    }

}