Developing iOS 8 Apps Using Swift – Interaction with multiple views (Part 6)

This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015

In parts 1 through 5 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven’t read that yet, check out Part 1

If not, and you just want to start from here, download the code for Part 5 to get started. We’ll use it as a template to begin.

In this tutorial we’re going to do quite a few things, so let’s get started!

Modifying The API Controller code

First off, our actual plan for this app is to show iTunes music information. So let’s modify the API controller to better handle this information.

One thing’s been bothering me though. When we create our APIController, we set a delegate after it’s created. But, an API Controller without a delegate isn’t all that useful, so why even offer the option to make one?

Let’s add a constructor that accepts the delegate as it’s only argument.

init(delegate: APIControllerProtocol) {
    self.delegate = delegate
}

Now, our delegate variable in the APIController isn’t actually going to be an optional any more. There is no APIController without a delegate!
So also change the delegate property to be an every day, non-optional APIControllerProtocol object.

var delegate: APIControllerProtocol

There’s also going to be an error at the end of the searchItunesFor method, because we’re treating the delegate object as an optional, but it’s not optional any more. So change the erroneous line to say this:

self.delegate.didReceiveAPIResults(results)

The only difference is we removed the ? from after the delegate property, to indicate it’s not an optional.

Now in our SearchResultsController, we need to change a few things. First, since the APIController constructor now needs the delegate object to be instantiated before *it* can be instantiated itself, we need to make it an implicitly unwrapped optional, and wait until viewDidLoad to assign it.

So in the api variable declaration change to this:

var api : APIController!

In the viewDidLoad method we need to unwrap the api object in order to call searchItunesFor(). You should end up with this

override func viewDidLoad() {
    super.viewDidLoad()
    api = APIController(delegate: self)
    api.searchItunesFor("JQ Software")
}

 

Searching for Albums
Let’s also modify our call to the searchItunesFor() in the APIController to use a search term for music. We’ll also show a networkActivityIndicator, to tell the user a network operation is happening. This will show up on the top status bar of the phone.

override func viewDidLoad() {
    super.viewDidLoad()
    api = APIController(delegate: self)
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    api.searchItunesFor("Beatles")
}

Now in our urlPath in the APIController, let’s modify the API parameters to look specifically for albums.

let urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music&entity=album"

We’ll now get results in the form of Album data, but this schema is a little different from apps. In fact running the app right now will we’ll just get default cells because the expected JSON data isn’t there. This is really fragile code, but we can make it slightly less fragile by doing some modeling of the data we expect.

Creating a Swift model for iTunes Albums

In order to facilitate passing around information about albums, we should create a model of what an album is exactly. Create a new swift file and call it Album.swift with the following contents:

import Foundation

struct Album {
    let title: String
    let price: String
    let thumbnailImageURL: String
    let largeImageURL: String
    let itemURL: String
    let artistURL: String
    
    init(name: String, price: String, thumbnailImageURL: String, largeImageURL: String, itemURL: String, artistURL: String) {
        self.title = name
        self.price = price
        self.thumbnailImageURL = thumbnailImageURL
        self.largeImageURL = largeImageURL
        self.itemURL = itemURL
        self.artistURL = artistURL
    }
}

It’s a pretty simple struct, it just holds a few properties about albums for us. We create the 6 different properties as strings, and add an initializer that sets all the properties based on our parameters.

So now we have a struct for albums, let’s use it!

Using our new Swift Album model

Back in our SearchResultsController, let’s modify the tableData array variable, and instead opt for a Swift native array for Albums. In swift, this is as easy as:

var albums = [Album]()

We can do away with do line var tableData = [], we won’t be using that any more.

This creates an empty array containing strictly Albums. We’ll now need to change our tableView dataSource and delegate methods to understand albums.
In the numberOfRowsInSection method, let’s change the number of items to the count of albums in our albums array:

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

Now in cellForRowAtIndexPath, let’s swap out those dictionary lookups for a single album lookup:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as! UITableViewCell
    let album = self.albums[indexPath.row]

    // Get the formatted price string for display in the subtitle
    cell.detailTextLabel?.text = album.price
    // Update the textLabel text to use the title from the Album model
    cell.textLabel?.text = album.title
    
    // Start by setting the cell's image to a static file
    // Without this, we will end up without an image view!
    cell.imageView?.image = UIImage(named: "Blank52")
    
    let thumbnailURLString = album.thumbnailImageURL
    let thumbnailURL = NSURL(string: thumbnailURLString)!
    
    // If this image is already cached, don't re-download
    if let img = imageCache[thumbnailURLString] {
        cell.imageView?.image = img
    }
    else {
        // The image isn't cached, download the img data
        // We should perform this in a background thread
        let request: NSURLRequest = NSURLRequest(URL: thumbnailURL)
        let mainQueue = NSOperationQueue.mainQueue()
        NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
            if error == nil {
                // Convert the downloaded data in to a UIImage object
                let image = UIImage(data: data)
                // Store the image in to our cache
                self.imageCache[thumbnailURLString] = image
                // Update the cell
                dispatch_async(dispatch_get_main_queue(), {
                    if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
                        cellToUpdate.imageView?.image = image
                    }
                })
            }
            else {
                println("Error: \(error.localizedDescription)")
            }
        })
    }
    return cell
}

Then there is the didSelectRowAtIndexPath method that needs to be modified to use the albums array. But, actually we’re not going to need this any more, so let’s just delete the whole method.

Creating Album objects from JSON

Now, all of this is not much use if we aren’t creating our album information in the first place. We need to modify our didReceiveAPIResults method of SearchResultsViewController to take album JSON results, create Album objects, and save them in to the albums array. Since we have a model for Albums now, it makes sense to move this functionality in to the Album model itself. So let’s make a minor adjustment to didReceiveAPIResults and delegate the responsibility of construction the albums array to the Album class.

func didReceiveAPIResults(results: NSArray) {
    dispatch_async(dispatch_get_main_queue(), {
        self.albums = Album.albumsWithJSON(results)
        self.appsTableView!.reloadData()
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    })
}

Note that since this is where the api request comes to it’s conclusion, we also turn off the networkActivityIndicator.

Now in the Album.swift file we need to add a static method that creates a list of albums from a JSON list.

static func albumsWithJSON(results: NSArray) -> [Album] {
    // Create an empty array of Albums to append to from this list
    var albums = [Album]()
    
    // Store the results in our table data array
    if results.count>0 {
        
        // Sometimes iTunes returns a collection, not a track, so we check both for the 'name'
        for result in results {
            
            var name = result["trackName"] as? String
            if name == nil {
                name = result["collectionName"] as? String
            }
            
            // Sometimes price comes in as formattedPrice, sometimes as collectionPrice.. and sometimes it's a float instead of a string. Hooray!
            var price = result["formattedPrice"] as? String
            if price == nil {
                price = result["collectionPrice"] as? String
                if price == nil {
                    var priceFloat: Float? = result["collectionPrice"] as? Float
                    var nf: NSNumberFormatter = NSNumberFormatter()
                    nf.maximumFractionDigits = 2
                    if priceFloat != nil {
                        price = "$\(nf.stringFromNumber(priceFloat!)!)"
                    }
                }
            }
            
            let thumbnailURL = result["artworkUrl60"] as? String ?? ""
            let imageURL = result["artworkUrl100"] as? String ?? ""
            let artistURL = result["artistViewUrl"] as? String ?? ""
            
            var itemURL = result["collectionViewUrl"] as? String
            if itemURL == nil {
                itemURL = result["trackViewUrl"] as? String
            }
            
            var newAlbum = Album(name: name!, price: price!, thumbnailImageURL: thumbnailURL, largeImageURL: imageURL, itemURL: itemURL!, artistURL: artistURL)
            albums.append(newAlbum)
        }
    }
    return albums
}

I know this looks like a lot of new code, but actually there’s not much going on here. It’s really just looping through the list coming from allResults, and its grabbing values for some keys, and setting defaults if they’re missing.

The ?? operator used here is pretty neat. It works like this:

let finalVariable = possiblyNilVariable ?? "Definitely Not Nil Variable"

The finalVariable value is set to possiblyNilVariable if it is not nil. But if it is nil? It uses the value of the thing on the right-hand side of the ?? operator. In this case, the string “Definitely Not Nil Variable”.

We use this here in order to prevent getting nil values passed in to our Album.

On line 39, we create an Album object. On line 40 the album is added to the list.
Finally on line 43 the list of albums is returned.

If you run the app now you should see a new list of Album’s pop up. Cool, right?

 

Creating a second view

Now to actually show the details of an album, we’ll need a new view. First let’s create the class.
Add a new file called DetailsViewController.swift that inherits from UIViewController.

Our view controller will be pretty simple to start. We’re just going to add an album, and implement UIViewController’s init method as well as viewDidLoad().

import UIKit

class DetailsViewController: UIViewController {
    
    var album: Album?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

This code doesn’t do much yet, but that’s okay. We just need the class to exist in order to set up our storyboard.

Since we’ll be pushing views back and forth on the stack we’ll want a navigation bar as well. It’s hard to explain in text, and there is an NDA preventing me from showing parts of Xcode 6 in screenshots, so instead I created a short video demonstrating how to do this in Xcode 5. The process is nearly identical for Xcode 6 Beta, and is not under any sort of NDA.

In the video we did the following:

  1. Embedded our view controller in a navigation controller using the Xcode shortcut in the Editor menu, by clicking the view controller, then selecting Editor->Embed In->Navigation Controller
  2. Added a new view controller
  3. Set it’s class and storyboard ID to ‘DetailsViewController’
  4. Control+Clicked+Dragged from the table view cell in our first view controller to the new view controller we just created, and selected ‘show’ for the type of segue.

What this last step does is creates a segue on our navigation controller that pushes the new view on top of the stack. If you run the app now and click a cell, you should see this new view animate in.

Let’s build out a simple UI for this new view. It’ll contain a UIImageView that is 100×100 pixels, and a title Label. Drag these objects out of the object library and arrange them any way you like on the new view.

 

Providing the new view with Album information

When the storyboard segue fires off, it first calls a function on whatever view controller is currently on the screen called prepareForSegue. We’re going to intercept this call in order to tell our new view controller which album we’re looking at. Add the following in SearchResultsViewController:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let detailsViewController: DetailsViewController = segue.destinationViewController as? DetailsViewController {
        var albumIndex = appsTableView!.indexPathForSelectedRow()!.row
        var selectedAlbum = self.albums[albumIndex]
        detailsViewController.album = selectedAlbum
    }
}

What’s happening here is the segue parameter being passed in has a member called destinationViewController, which is our fancy new DetailsViewController we just created. In order to set the album member on it, we first need to cast it to DetailsViewController using the ‘as’ keyword as shown above.
Then, by using the indexPathForSelectedRow() method of our table view we can determine which album is selected at the moment this segue happens.
Using this information, well tell our detailsViewController which album was clicked before it is displayed.

Now I’m going to show you a pretty nifty feature of Xcode. We’re going to let it write some code for us.

Open up your storyboard again let’s start creating IBOutlets for our image view, label, button, and text view. On the top-right hand corner of Xcode there is the ‘assistant’ button. The icon looks like a bowtie and suit jacket. Clicking on this will open up a code window right next to your storyboard window. Make sure that one of the panels is showing DetailsViewController.swift, and the other is showing Main.storyboard.

Now, hold control, and click+drag from your image view to your code file. Just a line under your class definition for DetailsViewController. It’ll prompt you for a name, so let’s call it ‘albumCover’. The default options should be fine here. After doing this you should see this line of code newly added:

@IBOutlet weak var albumCover: UIImageView!

We just created a new IBOutlet, and now it’s connected to our storyboard’s DetailsViewController. How cool is that?

Do the same thing for the label you added to your view, and call it ‘titleLabel’.

Next, let’s modify viewDidLoad so that it will load in the info we’re being passed to our view objects, here’s the final DetailsViewController code:

import UIKit

class DetailsViewController: UIViewController {
    var album: Album?
    @IBOutlet weak var albumCover: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        titleLabel.text = self.album?.title
        albumCover.image = UIImage(data: NSData(contentsOfURL: NSURL(string: self.album!.largeImageURL)!)!)
    }
}

The @IBOutlets are the UI connections made by our storyboards, and our viewDidLoad method sets the title and album cover variables to load in from our Album object.

Now try running the app and taking a look. We can now drill in to details for albums and get a nice big detail view with the album cover and title. Because we pushed in a navigation controller, we also get a functional Back button for free!

If you made it this far, I want to personally congratulate you so let me know on twitter that you pulled it off! You are well on your way to creating real iOS apps with Swift.

I’ve decided this tutorial series is going to be expanded upon and refined, along with several other tutorials and essays on working with swift, Xcode, and Apple in a new book, which I have available for pre-order here. Also, I’ve decided to open up a new forum for all the followers of this tutorial.

Make sure to sign up to be notified of the new sessions.

The full source code for this section is available here.

In part 7, we set up a full Detail view with a working music player, and implement some great animations.

Go To Part 7 ->

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing iOS Apps Using Swift Part 5 – Async image loading and caching

This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015

In parts 1 through 4 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven’t read that yet, check out Part 1

This table is slow! Let’s speed it up.

So, we now have the functionality we’re looking for, but if you run this yourself you’ll see its super slow! The issue is that the images in these cells are downloading on the UI thread, one at a time, and they aren’t being cached at all. So let’s fix that.

Let’s start by adding a lookup dictionary as a member for our SearchResultsViewController class:

var imageCache = [String:UIImage]()
Dictionary syntax
This is the first time we’ve seen this syntax so let me explain real quick.
The type specified here is [String : UIImage], this is similar to the Objective-C NSDictionary type, a HashMap in Java, or just Hash in Ruby/JS, but in Swift it’s very strict about the type. It takes a String as a key, and stores a UIImage as a value; no other types will be accepted.

So for example, if I have an image named “Bob” set to the UIImage with the file name “BobsPicture.jpg”, I might add him to the dictionary like this:

imageCache["Bob"] = UIImage(named: "BobsPicture.jpg")

The UIImage(named: “Bob.jpg”) part is just to get a UIImage from the file named Bob.jpg, this is standard Swift syntax for local files, the dictionary uses a subscript to set or retrieve it’s values. So if I wanted to get that image of Bob back out, I could just use:

let imageOfBob = imageCache["Bob"]

The reason we add the two parentheses is to call the constructor to init the empty dictionary.
Just like how we use APIController(), we need to use [String : UIImage]().

Hope that makes sense, if not yell at me on Twitter about it.

Now, in our cellForRowAtIndexPath method, we want to do quite a few things, we basically want to access the images in our cache, or download them if they don’t exist while on a background thread. After a new image is downloaded, it should be stored in to the cache so we can access it the next time the image is needed, without needing to go start another download process.

Let’s start by moving the imgData call out of our optional chain, and instead put it inside of the block that updates the cells. We’ll do this so that we can use image data from our cache *or* perform a new download, depending on the situation. Here, we’re switching to using NSURLConnection’s sendAsynchronousRequest method in order to download the image data on a background thread.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as! UITableViewCell
    
    if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
        // Grab the artworkUrl60 key to get an image URL for the app's thumbnail
        urlString = rowData["artworkUrl60"] as? String,
        imgURL = NSURL(string: urlString),
        // Get the formatted price string for display in the subtitle
        formattedPrice = rowData["formattedPrice"] as? String,
        // Get the track name
        trackName = rowData["trackName"] as? String {
            // Get the formatted price string for display in the subtitle
            cell.detailTextLabel?.text = formattedPrice
            // Update the textLabel text to use the trackName from the API
            cell.textLabel?.text = trackName
            
            // Start by setting the cell's image to a static file
            // Without this, we will end up without an image view!
            cell.imageView?.image = UIImage(named: "Blank52")
            
            // If this image is already cached, don't re-download
            if let img = imageCache[urlString] {
                cell.imageView?.image = img
            }
            else {
                // The image isn't cached, download the img data
                // We should perform this in a background thread
                let request: NSURLRequest = NSURLRequest(URL: imgURL)
                let mainQueue = NSOperationQueue.mainQueue()
                NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
                    if error == nil {
                        // Convert the downloaded data in to a UIImage object
                        let image = UIImage(data: data)
                        // Store the image in to our cache
                        self.imageCache[urlString] = image
                        // Update the cell
                        dispatch_async(dispatch_get_main_queue(), {
                            if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
                                cellToUpdate.imageView?.image = image
                            }
                        })
                    }
                    else {
                        println("Error: \(error.localizedDescription)")
                    }
                })
            }
            
    }
    return cell
}

So what’s all this code mean? Let’s run through the changes real quick…

IMPORTANT!

Before we download the real image we set the cell’s placeholder image. This is required if you want the cell to actually include an image view. Otherwise even loading in our image later will not show up! Create a blank image (I’m using 52×52 pixels, but it doesnt matter much) and Import it in to your project by click+dragging a file from finder in to you Xcode project, name it Blank52, and then set our cell to use this image. You can just grab my image file here (right-click and save as…)

cell.imageView?.image = UIImage(named: "Blank52")

Now our app should be less prone to crashing, and will now always have an image cell.

Put Image Downloading On A Background Thread

Now we need to check our image cache to see if this image has been downloaded before. We use the optional binding to check for the existence of our image in the cache:

if let img = imageCache[urlString] {
    cell.imageView?.image = img
}

If the image doesn’t exist (and initially it won’t) we need to download it. There are a couple of ways to initiate a download. Previously we used NSData’s dataWithContentsOfFile, but here we’re going to switch to NSURLConnection’s sendAsynchronousRequest, more similar to how our API works. The reason being is that we want to send off lots of small requests for images real quick, and we want to do it in the background. So let’s do that.

Look at the line with a call to NSURLConnection’s static method sendAsynchronousRequest, which takes a function/closure as a parameter for completionHandler. The lines after this call represent a function that is executed only *after* the async request returns.

NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
    if error == nil {
        // Convert the downloaded data in to a UIImage object
        let image = UIImage(data: data)
        // Store the image in to our cache
        self.imageCache[urlString] = image
        // Update the cell
        dispatch_async(dispatch_get_main_queue(), {
            if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
                cellToUpdate.imageView?.image = image
            }
        })
    }
    else {
        println("Error: \(error.localizedDescription)")
    }
})

Inside the block we will get back a few variables: response, data, and error.

If an error exists, proceed to create a UIImage from the data using the UIImage(data: data) constructor.

Next, we set the image cache to save our fancy new image with a key of the image URL. Using the URL as the key means we can find the image in our dictionary any time it pops up, even in a completely different context.

self.imageCache[urlString] = image

Finally, we set the cell image, on the UI thread (main queue):

dispatch_async(dispatch_get_main_queue(), {
    if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
        cellToUpdate.imageView?.image = image
    }
})

You’ll notice we’re also using the cellForRowAtIndexPath() method here. The reason we use this is because sometime’s the cell that this code was running for may no longer be visible, and will have been re-used. So, to avoid setting an image on an unintended cell, we retrieve the cell from the tableView, based on the index path. If this comes back nil, then we know the cell is no longer visible and can skip the update.

Okay! Give the project a run and see our amazing new blazingly fast, silky smooth table view!

The complete code up to this point is available on Github as a branch ‘Part5‘.

To get updates on this tutorial in your email, subscribe here. I’m also working on a book filled with practical tips and tutorials on working with Swift to develop iOS 8 apps. It’s now available for pre-order here.

Part 6 will focus on adding a new View Controller that we can open up to, loading it with iTunes data.

Go to part 6 now ->

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing iOS Apps Using Swift Part 4 – Adding Interactions

This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015

In parts 1, 2, and 3 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven’t read that yet, check out Part 1Part 2, and Part 3. If you enjoy these tutorials, I’m also working on a book full of quality content for Swift developers, and it’s available for Pre-Order now.

Loading cells from a storyboard prototype

Okay, so first there is one more thing we need to clean up. The Table View Cell thats being created in the delegate function cellForRowAtIndexPath is not the most efficient way to create cells. In iOS we always want to use dequeueReusableCellWithIdentifier in order to get a cell out of memory if one is available, rather than creating a new one every time a cell is rendered. This helps make scrolling fast, and keeps the memory usage down.

First, we are going to be referring to this cell by a storyboard prototype, which in code is represented by a string.
So, near the top of the SearchResultsViewController class, add the following line:

let kCellIdentifier: String = "SearchResultCell"

Next, we need to do some more storyboard modifications.
In order for Swift to know what a SearchResultCell is, we need to specify the cell as a prototype cell in our storyboard, and set it’s reuse identifier to SearchResultCell. So open up your storyboard and select the tableview, change the number of ‘prototype cells’ to 1.

Next, we need to click the newly added prototype cell to select it in the attributes editor panel.
After you’ve selected it, in the attributes inspector change the Style to “Subtitle” and type in the identifier as “SearchResultCell”.

Finally, in our SearchResultsViewcontroller.swift file, we are going to replace the instantiation of the cell with the following:

let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as! UITableViewCell

What this is going to do is give us an already instantiated cell.

Run the app, and once again we have the exact same result, except now we’re loading the cell from a storyboard prototype, which allows us to do all sorts of cool stuff with it.

Making the cells do something!

Okay, now we have one more delegate function that UITableView is going to call on our delegate class, SearchResultsViewController. This one will look like this:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)

Whenever a cell is tapped, this function will run. So let’s grab out the iTunes data for the row that was clicked by accessing the index of the array set by indexPath.row, the integer id of the row clicked. And then let’s display the same info in a popup.

Add your didSelectRowAtIndexPath method like so:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    // Get the row data for the selected row
    if let rowData = self.tableData[indexPath.row] as? NSDictionary,
        // Get the name of the track for this row
        name = rowData["trackName"] as? String,
        // Get the price of the track on this row
        formattedPrice = rowData["formattedPrice"] as? String {
            let alert = UIAlertController(title: name, message: formattedPrice, preferredStyle: .Alert)
            alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
            self.presentViewController(alert, animated: true, completion: nil)
    }
}

Here, we’re setting rowData to the value of whatever the array object at the selected index is to first get the information in to rowData. Then, we set the name and formattedPrice variables based on the rowData dictionary. Next, we instantiate an UIAlertController object, and set it’s title, message, and add an “Ok” button to dismiss the alert. Finally we show the alert with self.presentViewController()

Give it a run and you should now be able to see the name and price of any app clicked show up in a popup window. Cool right?

The full code for Part 4 is available here.
Part 5 focuses on speeding up our Table View. To get updates on this tutorial in your email, subscribe here.

Go to Part 5 Now ->

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing iOS Apps Using Swift Part 3 – Best Practices

This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015

In parts 1 & 2 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven’t read that yet, check out Part 1 and Part 2. If you enjoy these tutorials, I’m also working on a book full of quality content for Swift developers, and it’s available for Pre-Order now.

In this section we’re going to take a stop and clean up some of the code we’ve created so far by removing our network logic from our view controller code, and fixing some other issues that will hurt our performance. This may not be the most glamorous part of writing a new app, but it’s important! Let’s do it…

If you’re looking something that goes a little deeper than these tutorials, I also have written a book and created video tutorials which are available now. If that’s more your speed, check out my Swift Book & Video Packages ».

Split up the code

First things first, let’s rename our View Controller to be something more meaningful. Open up your ViewController.swift file and replace the class name ‘ViewController’ with our new name, ‘SearchResultsViewController’. Rename the file as well to SearchResultsViewController.swift.

If you try to run the app from here you’ll get a crash. This is because our storyboard file hasn’t been updated to know about the new class! So open up the Main.storyboard, select your ‘View Controller’ object in the scene (the left-hand side nav), and then select the Identity Inspector (right-hand side, third button.)

From here let’s change the class from ‘ViewController’ to ‘SearchResultsViewController’. Now we should be back on track. Check that the project still works, and let’s proceed.

Let’s now move the API code out to it’s own class. Right click in the xcode navigation pane and select ‘New File…’. This one is going to be a regular Swift file, under the iOS->Source navigation option.

This one handles all API work, so I’m going to call it APIController.

Now let’s grab our searchItunesFor() function, and cut and paste it out of the Search controller and in to the APIController, inside of a new class ‘APIController’.

Your complete APIController.swift file should look something like this:

import Foundation

class APIController {
    
    func searchItunesFor(searchTerm: String) {
        
        // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs
        let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
        
        // Now escape anything else that isn't URL-friendly
        if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
            let urlPath = "http://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software"
            let url = NSURL(string: urlPath)
            let session = NSURLSession.sharedSession()
            let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
                println("Task completed")
                if(error != nil) {
                    // If there is an error in the web request, print it to the console
                    println(error.localizedDescription)
                }
                var err: NSError?
                
                var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
                if(err != nil) {
                    // If there is an error parsing JSON, print it to the console
                    println("JSON Error \(err!.localizedDescription)")
                }
                let results: NSArray = jsonResult["results"] as NSArray
                dispatch_async(dispatch_get_main_queue(), {
                    self.tableData = results
                    self.appsTableView!.reloadData()
                })
            })
            
            task.resume()
        }
    }
    
}

If you try to build this straight away you’ll see three errors:

1) searchItunesFor() is now undefined in our SearchResultsViewController.
2) self.tableData is now undefined in the APIController.
3) self.appsTableView is now undefined in the APIController.

To deal with this we need to let these objects know about each other. So let’s make APIController a child object of our SearchResultsViewController. It’s pretty easy to do, just add the following line underneath your SearchResultsViewController class definition:

let api = APIController()

Now modify the line that calls searchItunesFor() to this:

api.searchItunesFor("Angry Birds")

The only difference here is that we’re calling the method from an instance of an APIController, as opposed to doing this all from a method in the View Controller.

That takes care of the first set of errors, but now we need to also get APIController’s results back to the SearchResultsViewController. We would like for our API controller to be able to respond to arbitrary API calls, so we should define a protocol that views can subscribe to in order to get results!

Defining an API protocol

There are two erroring lines in our APIController right now referencing the tableData results, let’s just remove these. We’re going to use something a little cleaner.

dispatch_async(dispatch_get_main_queue(), {  // DELETE ME
    self.tableData = results                 // DELETE ME
    self.appsTableView!.reloadData()         // DELETE ME
})                                           // DELETE ME

Above the class definition in our APIController, let’s add a protocol that declare the function didReceiveAPIResults() as a required implementation. Note that the protocol goes outside of the class definition, so make sure it’s outside of the APIController{} curly braces.

 

protocol APIControllerProtocol {
   func didReceiveAPIResults(results: NSArray)
}

 

This doesn’t do anything on it’s own, but now we can add the protocol to our SearchResultsViewController. Not adhering to the protocol will now cause an error, so we don’t make the silly mistake of not implementing didReceiveAPIResults!

Adhering to the protocol

Now modify your SearchResultsViewController to adhere to the protocol:

class SearchResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {

Building now should give an error that SearchResultsViewController doesn’t conform to the APIControllerProtocol, great! This is exactly what a protocol does. It causes the compiler to spit out errors if you intended to implement a protocol, but didn’t completely do it. In this case it’s barfing because we’re missing the didReceiveAPIResults() callback method. Let’s add it…

We just need to add the function within our SearchResultsViewController class. It will look pretty much like the contents of our dataTaskWithURL closure from before.

func didReceiveAPIResults(results: NSArray) {
    dispatch_async(dispatch_get_main_queue(), {
        self.tableData = results
        self.appsTableView!.reloadData()
    })
}

The one thing left to do is change our API Controller to include a delegate object, and to call this method when the connection finished loading some API data.

Using the Protocol

Back in our APIController.swift file, let’s add the delegate and an empty constructor, just under the class definition.

var delegate: APIControllerProtocol?

The question mark at the end here indicates that delegate is an *optional* value. Without the question mark, we will get a compiler error for not using an initial value for this variable, but with it we’re okay. The delegate object can be of any class here, as long as it adheres to the APIControllerProtocol by defining the method didReceiveAPIResults(), as we have done in our SearchResultsViewController

Now that we have the delegate variable added, let’s head back to our SearchResultsViewController, and in the method viewDidLoad, let’s set the delegate for our api controller to itself, so it will receive delegate function calls.

api.delegate = self

Finally, in the dataTaskWithURL closure inside of our searchItunesFor() method, let’s add our protocol method where our tableview reloading code used to be. This will pass the responsibility of reloading the UI to the delegate, which in this case is SearchResultsViewController.

This goes in place of where we delete the lines refreshing the tableview earlier on.

if let results: NSArray = jsonResult["results"] as? NSArray {
    self.delegate?.didReceiveAPIResults(results)
}

Here is the completed APIController.swift code up to this point:

import Foundation

protocol APIControllerProtocol {
    func didReceiveAPIResults(results: NSArray)
}

class APIController {
    var delegate: APIControllerProtocol?
    
    func searchItunesFor(searchTerm: String) {
        // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs
        let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
        
        // Now escape anything else that isn't URL-friendly
        if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
            let urlPath = "http://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software"
            let url = NSURL(string: urlPath)
            let session = NSURLSession.sharedSession()
            let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
                println("Task completed")
                if(error != nil) {
                    // If there is an error in the web request, print it to the console
                    println(error.localizedDescription)
                }
                var err: NSError?
                if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
                    if(err != nil) {
                        // If there is an error parsing JSON, print it to the console
                        println("JSON Error \(err!.localizedDescription)")
                    }
                    if let results: NSArray = jsonResult["results"] as? NSArray {
                        self.delegate?.didReceiveAPIResults(results)
                    }
                }
            })
            
            // The task is just an object with all these properties set
            // In order to actually make the web request, we need to "resume"
            task.resume()
        }
    }
    
}

Common issue here

*** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key appsTableView.’

If you get this issue, it could be caused by a few bugs in Xcode surrounding the renaming of View Controller subclasses. The best way to fix this is to open up the storyboard, select the ‘Search Results View Controller’ object. Then in the identity inspector change its class back to UIViewController, build the app, then change it back to ‘SearchResultsViewController’. This resets some things and fixes the issue.

PHEW!

Okay, I know that might have seemed like a lot of boilerplate, and now our app does the exact same thing as it did before, but now we have something much more flexible. We can now use APIController for any API call to the iTunes search API, and have a custom delegate get the response. I think we ran out of time on this one, so we’ll move on to the interaction in Part 4. If you want to follow along make sure to sign up for them via e-mail.

I felt this step was more important than fixing the performance issues, but I promise we’ll speed things up before we’re done. There are just some things we need to get done first. Hold tight!

Full code for this section visible here.

Go to Part 4 ->

 

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Developing iOS Apps Using Swift Tutorial Part 2

This section completely updated to reflect changes in Xcode 8.3.1, as of April 17, 2017

In part 1 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some text inside of them. If you haven’t read that yet, give it a read here.

For this section, we’re going to do something a little more ambitious. We’re going to hit the iTunes Search API for the iTunes Store, download the JSON results, parse them in to Dictionaries and then populate our Table View with this information. Then, we will add some user interaction by adding a click event to the tableview, so that when an item is clicked the iTunes store item will be opened.

If this sounds like a lot of work, don’t sweat it. This is pretty basic functionality for iOS apps and it’s one of the most common things any developer has to do. Let’s get going…

Connecting the UI

The first thing we need to do is get a reference to our tableView, so it can be used from code. Go ahead and add this line to your ViewController.swift file, just under the class definition, but outside of any functions.

@IBOutlet var appsTableView : UITableView!

This bit of code allows up to connect our Table View in our Storyboard to this variable, “appsTableView”. Save this file and open up your storyboard. Now select the View Controller object (the one with a yellow icon) and in the right-hand side pane click the last tab, the Connections Inspector. Here you should now see an outlet for “appsTableView”. Click and drag from the dot next to this outlet on to the Table View in our scene.

Connecting the Table View to the View Controller

Let’s also add a variable to hold the table data itself. Just under the class definition for ViewController add:

var tableData = [[String: String]]()

This variable is an Array type that contains multiple Dictionary types (or hashable types if you prefer). Inside these values the key is of type String as well as the value. Or in other words I can get or set String values by accessing this variable with any String key in the dictionary, as we’ll see in a moment. If that does’t make sense just yet, just keep going and you’ll see how it’s used later in the tutorial.

Making the API Request

Now that we have the UI connected, we’re ready to make an API call. Create a new function called searchItunesFor(searchTerm: String). We’ll use this to make our requests happen for arbitrary search terms.

To keep this tutorial short, I’m going to just post my final code and let the comments do some of the explaining. I’ll also break it down line-by-line afterward. Also, I’m always open to questions and further discussion in the comments though, so feel free to chime in!

func searchItunes(searchTerm: String) {
    // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs
    let itunesSearchTerm = searchTerm.replacingOccurrences(of: " ", with: "+", options: .caseInsensitive, range: nil)
    // Also replace every character with a percent encoding
    let escapedSearchTerm = itunesSearchTerm.addingPercentEncoding(withAllowedCharacters: [])!
 
    // This is the URL that Apple offers for their search API
    let urlString = "http://itunes.apple.com/search?term=\(escapedSearchTerm)&media=software"
    let url = URL(string: urlString)!
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            // If there is an error in the web request, print it to the console
            print(error)
            return
        }
        // Because we'll be calling a function that can throw an error
        // we need to wrap the attempt inside of a do { } block
        // and catch any error that comes back inside the catch block
        do {
            let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
            self.didReceive(searchResult: jsonResult)
        }
        catch let err {
            print(err.localizedDescription)
        }
        // Close the dataTask block, and call resume() in order to make it run immediately
    }.resume()
}

First, we fix some issues with the search terms. For one thing, the iTunes API is not going to like seing spaces, instead it wants plus signs (+), so the replacingOccurences function does this work, and we put the result in the itunesSearchTerm variable.

Next, we also want to make sure no other characters will cause issues. For example a question mark (?) may be viewed by the API as the end of the request if it was included in the search term. So we use the method addingPercentEncoding to encoded every character in to a percent-encoded form, and store this new value in to escapedSearchTerm.

After that we define our URL, which is just the iTunes search API URL with the escapedSearchTerm inserted in to the term field. We store this in the variable urlString. Next, we convert this URL String in to a proper URL object. This will basically validate that the URL is valid, or at least properly-formed, and then it gives it some useful methods as well that are common to URLs.

Now comes the network request. We make the request from the URLSession API that Apple provides. You could create a URLSession for this task, but Apple’s API will provide a pre-constructed one called shared. We access this with the simple dot-notation URLSession.shared.

From here, we use the shared sesion to create a new Data Task by calling it’s dataTask function with the url we created the line before. The next part is the completion handler. This itself is a function we define inline, inside of the curly braces.

So, everything here inside of the curly braces, where the indentation is increased, is only executed after the Data Task has been completed. Hopefully when this happens, what we get back from the server is JSON data containing the search results! It’s also possible that there was some kind of error, so we check whether or not an error is present by using the optional binding syntax:

if let error = error {
...

This syntax replaces the optional error: Error? with an unwrapped optional error: Error, but only if it exists. Most of the time, the value of error will be nil, and this block of code will be skipped entirely.

Next we are going to decode the result from a Data object in to a dictionary we can use to access each JSON element in the results.

Because the Apple APIs for JSON deserialization can throw an error, we perform this work inside of a do { } block, with a catch let err block after it. If something goes wrong during JSON deserialization, the catch block will be executed. But if everything goes to plan, the do block will complete executing.

Once we get the deserialized data back, we call a new method self.didReceive(searchResult: jsonResult).
This method is not defined yet, so we’ll define it next.

Finally we call resume() at the end of the dataTask block in order to execute the API request immediately.

Getting the data we need out of the JSON response

Now that we are getting a JSON response back, we will want to take out the values we care about. In this case that’s the price, a thumbnail, and the name of the app. So next, let’s create the didReceive method inside our ViewController class in order to parse out this data and store it inside the tableData variable that will inform our Table View.

func didReceive(searchResult: [String: Any]) {
    // Make sure the results are in the expected format of [String: Any]
    guard let results = searchResult["results"] as? [[String: Any]] else {
        print("Could not process search results...")
        return
    }
 
    // Create a temporary place to add the new list of app details to
    var apps = [[String: String]]()
 
    // Loop through all the results...
    for result in results {
        // Check that we have String values for each key we care about
        if let thumbnailURLString = result["artworkUrl100"] as? String,
            let appName = result["trackName"] as? String,
            let price = result["formattedPrice"] as? String {
            // All three data points are valid, add the record to the list
            apps.append(
                [
                    "thumbnailURLString": thumbnailURLString,
                    "appName": appName,
                    "price": price
                ]
            )
        }
    }
    // Update our tableData variable as a way to store
    // the list of app's data we pulled out of the JSON
    tableData = apps
    // Refresh the table with the new data
    DispatchQueue.main.async {
        self.appsTableView.reloadData()
    }
}

Here we are first off checking that we have the right data type. The results argument passed in to the didReceive function here is our JSON value returned from the iTunes Search API. We are going to want to check for three keys:

  • artworkUrl100
  • trackName
  • formattedPrice

Why these keys? This is just the keys Apple chose in their API docs. If you take a look at a sample API response you’ll see keys contain the info we want about an app.

So what we do is we loop through everything inside of the array of results, and then check that each of these three values are present, and are convertible to a String type. Here we’re using a compound form of the optional binding syntax that allows us to check for the valid presence of multiple values. if let varA = valueA, let varB = valueB { }. Not only are we checking for their presence, but if they can be represented as String objects.

If all three keys are present, and they can all be represented as strings, then we have an app record! We’ll add it to the list of apps, a temporary variable we created to store each new app we come across for this query.

I’m using slightly different keys for our app’s purposes. This is mainly just to show there is no reason these keys must match.

Finally, once the list of apps is completely updated, we assign the tableData variable that is going to be used to hold the Table View’s data to the array of apps we just created. From there, we call reloadData() on the appsTableView, which let’s the table know there is new data to work with, and that it should re-render itself.

You’ll notice the reloadData() is inside of another block. This one is from the Grand Central Dispatch API. By putting this code inside of the block DispatchQueue.main.async { }, we’re able to assure this is executed on the foreground thread. Because this code is all being executed in response to a network request (the API call), we end up on a background thread. This is because we know we have to wait a moment for the API to respond. The iOS App won’t just freeze the app up while it waits for the response. Instead, it will send the response in to a background thread.

So, now that we are actually updating some UI, we want to jump back on to the main thread and make sure it shows up immediately. If you are curious, you can call reloadData outside of this block and see how it effects the performance of the app (after we update the actual UI in the next step)

Update the UI with the new data

Now that we’ve got our API call updating the tableData variable, and calling reloadData() on the appsTableView we connected earlier, we can proceed to actually implement those UITableViewDataSource methods we set up in the beginning of the tutorial…

You may remember from last time we implemented the function for our Table View that we had a count method, which determines the number of rows; and a cell method, which actually creates the cell and modifies it for each row.

We’re going to update these now to use the data we pulled down from the web.

Swap out your methods with these two functions:

// MARK: UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableData.count
}
 
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "MyTestCell")
 
    // Get the app from the list at this row's index
    let app = tableData[indexPath.row]
 
    // Pull out the relevant strings in the app record
    cell.textLabel?.text = app["appName"]
    cell.detailTextLabel?.text = app["price"]
 
    return cell
}

The first one is pretty simple. The number of rows should be the same as the count of the records in tableData (the number of app returned from the API).

The second function is what creates the cell, which is what is displayed in the Table View. First we call the constructor to make a fresh UITableViewCell to work with. Then we retrieve the current app that should be displayed by accessing tableData[indexPath.row]. The variable indexPath is passed in as an argument by Apple’s Table View class. It represents the current index that it is asking for a cell for. It’s kind of a backwards way of thinking about it, but basically this function is the table itself asking for an individual row. Which row? Well… the one at indexPath.row.

Next, we’ll set the text values for the text and detail labels of the cell. We’ll set these to be the value of the app array we created earlier in response to the API response.

Call the API

As a final step, we’ll manually make the API call for our searchItunes method by adding it inside of the viewDidLoad function. The viewDidLoad method is called on this View Controller object any time the screen it represents is being constructed. This is a common pattern in iOS development.

Find your viewDidLoad function (the Xcode template will have already added it) and add a call to the searchItunes function.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    searchItunes(searchTerm: "JQ Software")
}

It’s alive!

Finally, run the app! Do you see the search results? You should see a few of my apps from the App Store pop up if you used my JQ Software search term. Try some other search terms and rebuild the app to see the different results that come back. You’ll notice we don’t yet have images in place, but we’ll get to that in later sections. We did get the data for that already after all.

If you run the App using JQ Software as a search term it will look like this at this stage:

JQ Software Apps List

Next time in Part 3 we’ll work on the interaction piece of the app, allowing users to search for anything they like, and making the table cells clickable!

Download the Source Code

Go to the Next Part

Go to Part 3 now ->

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Swift Tutorial – Developing iOS Apps Part 1

Swift Tutorial

iOS Development moves to Swift

This tutorial updated to reflect changes in Xcode 8.3.1 and iOS 10, as of April 17, 2017

Apple announced at WWDC a major change to the way iOS apps are developed, it can now be done using a new programming language called Swift. In my efforts to adapt and learn the language’s common patterns and best practices, I will be posting regularly as I step through the learning process, sharing everything I find. I hope you decide to follow along!

In this tutorial we will build an iOS application that will pull down iTunes tracks from the iTunes web API (or application programming interface), display that information on the iPhone using a common UI element called a Table View, and finally allow us to drill down in to an album and play some of the tracks.

Tutorial Index

So I’m going to start with a pretty basic app and explain how the code works. Ready? Here we go…

The Basics

Swift opts to use a Javascript-like ‘var’ keyword to define any new variable.

So for example to create a new variable with a bit of text in it, you would have this:

var myString = "This is my string."

This is declared with a var so the variable can change at any time, similar to most languages.

However the let keyword creates constants. These can not ever be changed once they are defined. If you try, a compiler error will appear and the program just won’t run.

let someConstant = 40

In this case kSomeConstant is implicitly defined as an integer, or Int. If you want to be more specific you can specify which type it is like so:

let someOtherConstant: Int = 40

With both arrays and dictionaries, they are described using brackets []

var colorsArray = ["Blue", "Red", "Green", "Yellow"]
var colorsDictionary = ["PrimaryColor":"Green", "SecondaryColor":"Red"]

You can access the members of an array with integer value indexes, and the members of a dictionary with String keys (or other types, but that’ll come in later tutorials)

let firstColor = colorsArray[0]
// firstColor is now "Blue"
let aColor = colorsDictionary["PrimaryColor"]
// aColor is now "Green"

There’s a lot more to go over, but I think these basics are important to get a start going on to the tutorial. So with that, let’s move on to Hello World. If you want to play around with this a bit yourself before getting going on our first iPhone App. Be sure to check out the Playground containing this sample code on Github.

Hello World

Hello World in Swift and iOS

First, we’re going to write the simplest app imaginable to get started, Hello World. This segment comes directly from my upcoming book on Swift development, but it’s so important and fundamental to getting started I thought it would be good to release for free here.

Our app will only do one thing: print “Hello World” to the console. You’ll need a developer copy of Xcode in order to follow along, which requires a developer account. If you have one, head on over to http://developer.apple.com and get your copy before we begin.

So now you’ve got your IDE set up. Let’s write hello world out to the console. This example demonstrates the simplest app that can be built, and more importantly shows that you’re environment is set up correctly.

Set up an Xcode project using the single-view application template, and make sure you opt for Swift as the language.


You should now find an AppDelegate.swift file in the project hierarchy. Inside of this file find the line that says:

"// Override point for customization after application launch."

Replace this line with our amazing hello world code:

print("Hello World")

Hello World Swift Code

Now press run and you should see a blank app boot up, and the words “Hello World” print to the console.
Note that this will not show up in the iPhone simulator. Look at the bottom of your Xcode window and you’ll see a console that says ‘Hello World!’.

Hello World Output

Congratulations! You just wrote your first app in Swift! This app probably won’t win any awards, let’s trying doing something a little deeper…

Adding a Table View

In this section, we’re going to actually put some stuff on the screen.
Open up your Main.storyboard file in Xcode and lets drag in a “Table View” object from the Object Library (don’t use a table view controller.) Position this fullscreen in your app window and make sure it lines up with the edges. Then resize the height by dragging down the top edge and giving a little bit of space (this gives room for the status bar at the top of the phone.) If you run the app at this point, you should see an empty table view in the simulator.

UITableView drag on to StoryBoard

The empty table view in the iPhone Simulator:

The empty table view in the iPhone Simulator

Now we need to set up a delegate and data source for the table view. The Data Source is an object that informs the Table View of which data to show. The delegate let’s us handle interactions such as tapping a row on the Table View.

Setting these outlets is easy to do in interface builder. Just hold control, and then click and drag from the tableview to the “View Controller” object in your storyboard’s hierarchy, and select ‘data source’. Repeat with the ‘delegate’ options.

Setting up the UITableView Data Source and Delegates

I’ve received a ton of questions about this, and many people reporting errors about the table view not being set, so to make things a little easier I made a quick video showing how connecting Storyboard objects to your code works. Make sure to go fullscreen and select the 720p option to make sure you can see what’s happening. This will look slightly different from the Xcode interface you are using, but functionally all this works the same.


Connecting Storyboard Objects to Code in Xcode

Okay, now let’s dig in to the protocol methods for Table Views. Because we’re using the UITableViewDataSource and UITableViewDelegate in our view controller, we need to modify the class definition to say as much.

So open ViewController.swift and modify this line:

class ViewController: UIViewController {

to this:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

This will cause some errors to pop up, but don’t worry this is expected. In fact, the errors are the primary purpose of indicating these protocols on our class. It lets us know that we aren’t done actually implementing the UITableViewDataSource or the UITableViewDelegate yet.

Command+clicking on either of these protocols will show the required functions at the very top. In the case of a tableview dataSource, we need at least these two:

public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

So let’s modify our View Controller class by adding these two functions.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}
 
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "MyTestCell")
 
    cell.textLabel?.text = "Row \(indexPath.row)"
    cell.detailTextLabel?.text = "Subtitle \(indexPath.row)"
 
    return cell
}

As a handy trick, when you need to write these long methods for cases like this, you can just start typing the function in Xcode, focusing on the unique part of the name such as “cellForRo…” and Xcode will usually auto-complete what you were typing.

The first method is asking for the number of rows in our section, in this simple tutorial we just hard-code 10, but normally it would be the length of an array controller. This example is intentionally simple.

The second method is where the magic happens. Here we create a new instance of a UITableViewCell called cell, using the Subtitle cell style.
Then, we assign the text value of this cell to the string “Row #(indexPath.row)”
In Swift, this is how variables are embedded within a string. What we’re doing is retrieving the value of indexPath.row by inserting (indexPath.row) in to our string, and dynamically replacing it with the row number of the cell. This allows results such as “Row #1″, “Row #2″, etc.

The detail text label is only available in the Subtitle cell class, which we are using here. We set it similarly to “Subtitle #1″, “Subtitle #2″, and so on.

Go ahead and run your app and you’ll now see an amazing list of cells with titles and subtitles indicating their row numbers. Don’t see the cells? Make sure you connected your storyboard delegate and data source to the ViewController class. See the Youtube video here

This is one of the most common ways to display data in iOS, and will be sure to serve you well. For the full code to my View Controller file, take a look at the full source for this section on github.

You’re doing great!

Made it this far and everything is working? Awesome! If not, let me know if you got stuck. I’m happy to help 🙂

In my Swift book we spend some more time going over the basics, as well as delving much deeper in to the advanced details. Learn more about it here.

In part 2, we’re going to explore using the iTunes search API to create an app capable of finding and displaying albums within the iTunes store.

Full code for this part here »

Go To Part 2 »

Get The Source
Next Part

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Mobile Gaming: The Next Frontier for Online Gambling Sites

From poker to blackjack, developers are taking to the mobile market

Last year, the International Data Corporation reported that growth in the worldwide mobile market grew 6% in the second quarter of 2013 (2Q13), with vendors shipping upwards of 432.1 million mobile phones to markets across the globe. Thanks to new vendors entering the market, smart phones and mobile computing have become more accessible to a wider audience.

 

There was also a 52.3% year-over-year growth in smart phone sales, with vendors shipping 237.9 million units in 2Q13, compared to the 156.2 million units shipped in 2Q12. Studies by Super Monitoring also show that 91% of the global population now own mobile phones, with 56% owning a smart phone. If the growth and prominence of the mobile market wasn’t apparent before, it sure is apparent now.

Mobile markets have allowed many new developers to begin offering gaming at all levels, from casual to hardcore. Research by MobiThinking revealed that games account for 145/300 of the top apps available on iTunes, and 116/300 of the top apps on Google Play. Revenues for apps and games are estimated at $25 billion in 2013 alone – a figure that’s set to triple by 2017.

 

Thanks to this, even companies and developers who had previously seen success in other industries have begun closing in on the mobile market. The online gambling industry recently made great strides when some companies were granted licenses to operate in New Jersey, Nevada, and Delaware, but the expansion hasn’t ended yet. Online gambling companies have already released simple slot game simulations and other games that rely on luck, but the push for better quality mobile games is felt across all sectors. Some companies have been successful in this endeavor, while some have not.

 

Bwin.party, who released their partypoker iPhone app last year, says that one of the main points hopefuls needed to develop for any mobile app to be successful was ease-of-access. Connectivity remains to be one of the best features of mobile gaming, and with mobile games, players expect faster, smoother gameplay and results. To address this, bwin.party decked out a Fast Forward type of game specifically for their mobile users – a feature clearly lacking from the apps that had failed.

 

Could the mobile market be the next frontier for online gambling? Ness Software sure seems to think so, saying that as the number of mobile users go up, the only thing left for a smart company to do is join them and go mobile. Nowadays, consumers want something that lets them do their business – be it banking, emailing, or gaming – on the go, and lets them do it fast. And if online gambling companies want to stand a chance, they’d better take their business to the app stores.

 

Infographic 2013 Mobile Growth Statistics

Infographic 2013 Mobile Growth Statistics

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

Using open source iPhone app components with Cocoa Pods

For a while after the initial release of the iPhone App Store and it’s SDK, there was not much in terms of open source code to use and learn from. But times have changed, and these days there is a huge database of open source components, and even full projects ripe for use in your next app.

This post is a bit of an instructional guide for those looking to take advantage of open source in their iPhone projects. Whether you are new to iOS, a seasoned developer, or a project manager, you can benefit from this short guide.

Currently, the biggest repository of open source iOS components is CocoaPods. The official list of components, known as “Pods’, is maintained in a Github repository located here: https://github.com/CocoaPods/Specs

Unfortunately this list is not particularly easy to browse. To find out the details of any one Pod, you have to select one from the list, pick a version, open the.podspec file, and then pick out the description in the file. Fortunately for you, the handsome reader, you can browse a (possibly slightly out of date) list of these Pods on the site https://www.cocoacontrols.com/cocoapods, in a much more easy to digest format.

So how do you use these? If you’ve got a ruby install set up with ruby gems, you can just navigate to your project directory and create a Podfile file, with no extension. It looks like this:

platform :ios, ‘6.0’

pod ‘TestFlightSDK’, ‘>= 1.1’
pod ‘SVProgressHUD’
pod ‘iRate’
pod ‘TimesSquare’, ‘1.0.1’
pod ‘AFNetworking’, ‘1.1.0’
pod ‘iCarousel’

Once you’ve created this Podfile you can run this command in Terminal:

$ pod install

If it gives you some kind of error, you might need to install Cocoa Pods. If that’s the case you first need to run this:

$ gem install cocoapods

And if that doesn’t work, then you still need ruby gems, and maybe even ruby.

Once you generate the pod install, make sure you close your Xcode project if you already have it open, and from now on use the .xcworkspace file when working on your project. What you’ll find is that Cocoa Pods has now created a subproject for your Pods. Yay! This means you can now compile your dependencies separately from the project, and changes to your project shouldn’t call for a full recompile. More importantly, you can easily update your dependencies by just modifying your Podfile, and running ‘pod install’ again.

So to recap:

1. Install ruby

$ \curl -sSL https://get.rvm.io | bash -s stable

2. Install ruby gems if you don’t have ut

Visit http://rubygems.org/pages/download

3. Install cocoapods

$ gem install cocoapods

4. Create a Podfile in your Xcode project directory

5. Add any relevant pods you might want to use. At this stage I do not specify a version, I let it use the most recent, and then lock it to that version to avoid unwanted updates. I’ll later remove the version specification when I feel it is time to get everything up to date.

6. Run pod install

$ pod install

7. Open your project from the xcworkspace file instead of the xcodeproj file.

8. Enjoy!

Related article: 8 Great Open Source Projects to use in your next iPhone App

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

5 more things I wish I knew when I released my first iPhone app

Haven’t read the first part? Read Part 1 and then come back, we’ll be waiting.

Okay, read that? Here we go!

 

6. Wise SEO choices can have a huge impact on sales

As any Stellar SEO expert will tell you, iOS App SEO is not exactly like web SEO. The rules are much more simplistic, based on fewer factors than google search for example. I’ve found that the App Store ranks search terms on a few key factors:

– Title

– Company name

– App description

– Keywords

I would venture to say they are even prioritized roughly in that order. To get started optimizing your keywords on the app store, you can check out your competition using https://sensortower.com/. It’s a free and easy site to quickly get a good estimation of the keywords other apps are using. This may give you some ideas for keywords to use in your own app. Testing it with some of my own apps, I can say the results are fairly accurate.

7. Piracy is a very real concern

I remember the first day I released in-app purchases for one of my apps, PhotoGoo. Users could now purchase “stickers” to place on their photos to make funny faces and pictures. When my analytics reports started coming in I could see there were many purchases being made! I was thrilled. The minimum cost of all these purchases to deliver content to the device would surely be lower than the amount users were paying, except one problem:

The next morning I woke up to find nearly 1% of the purchases actually showed up in my iTunes reports. Upon further investigation I discovered mass piracy causing my analytics to disagree with my iTunes reports, and it was costing me money.

Now, in this case it wasn’t costing much. All I was doing was delivering some png files to the users, and not very big ones. But, it’s important to note that the cost of bandwidth and other such services may be impacted by piracy if you are selling premium content through IAP, or just using your bandwidth to serve customers who you *assume* are paying you.

8. App development is expensive.

This one gets asked often, “How much would it cost to build and app that does xyz?” I may do a full follow-up post on this help people asking this question find a rough answer. But in short, the simplest of the simple apps will be a few thousand dollars to pay a developer to write, and anything substantial could be $30k, $50k, or more depending on the app. The technology is pretty recent, so there is also not much competition in the space of app development. If you want to get an app developed, contact some developers with your idea (and wireframe it as well as you can.) See what their average prices are, but just know it’s a relatively big investment.

9. Planning is key

This is one if the most common problems I find in app development. Now, I’m not advocating that we all abandon agile development processes. In fact I’m not talking about programming right now. What needs to be locked down before beginning a project is a solid business strategy. This strategy may change over time, but the developers and stake holders need a goal. It goes beyond technology, because technology is just implementing features, and feature are just implementing business logic, which are usually just selling points for a product. Most software development can be traced back to it’s original business value, and if it can’t… well then why are you building it?

Having great planning can be the difference between the MVP you need, and the bloated unpolished product nobody wants. Yes, I group early product testing in to this category as well. It’s important not only to plan the technical details of your product, but trace it back to marketing objectives. What do the customers really want? Hint: They don’t know.

10. The apps in the top 10 aren’t the only ones making money

Based on my own apps, and many conversations I’ve had with other app developers, I can tell you that being in the top 10 doesn’t neccessarily need to be the goal. Sure, it would be great to have a top 10 app, but there is plenty of money to be made in the 300th spot on the iPad Photography charts. I think it’s important to remember that you can make money without being the biggest app in existence. If you set your goals to be slightly less lofty, it will increase your odds at being successful in the development process. You may even find that you end up making plenty of revenue this way.

 

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS

4 Steps to Design Marketing into your App

This guest post written by Mike Davis, CEO & Founder of StoryPress

1. Think through the whole sale process before you write any code, in fact it should be a critical component of your UI.  Many people just “know” that it will be an IAP or ads and completely ignore it until the end.  Designing the sale process is as important as anything else and deserves the same level of attention as your apps unique or differentiating feature.

2. Think about social sharing in the UI stage as well.  When people are registering can you ask them to invite their friends?  Maybe show their contacts and/or Facebook friends with 1 click to invite all?  Designing this type of social impact can be critical to your success and not something you want to throw in after the fact.

3. Where will your app be sold?  Is it only inside the app or on the app store?  Many of the leading app brands leverage their website and social media to generate sales.  With tools like AppKeyz now available, you can create a product page on Facebook and a mini-site capable of generating sales to promote on social media, including Facebook and Twitter.  Use these mini-sites to reward social sharing with 2 for 1’s or discounts on your apps.

4. Don’t waste your money on posters and t-shirts at trade shows.  It’s too much clutter and too expensive to stand out from the crowd.  Similarly expensive and extravagant launch parties almost never generate enough buzz or sales to come close to paying for themselves.  A better investment would be a PR person and several way placed interviews or articles.

Related article: Making Apps That Spread

Did this tutorial help you?

Support my Patreon

Your support on Patreon allows me to make better tutorials more often.

Subscribe via RSS