Core Data in Swift Tutorial (Part 2)

This post compatible with Xcode 6.3, Updated on February 16, 2015

This is part two of a tutorial series covering the usage of Core Data in Swift to write iOS apps with persistence. If you haven’t read part one yet, read that first.

If you really want to get your feet wet, my Swift book which is now available for pre-order with early access.

Creating more records

First off, if we’re going to do anything more interesting than just showing a single record pop up in an alert, we’ll need to generate some more records to play with. Let’s open up our LogItem.swift file and add a helper function for adding new records.

Basically, we want to be able to simply call a method on the LogItem class, pass in some parameters, and get a new LogItem back.

For example, when we’re done the actual function call might look like this:

let newItem = LogItem.createInManagedObjectContext(managedObjectContext, "Item Title", "Item text")

The LogItem class is not tied to any specific NSManagedObjectContext, so we want to make sure we aren’t storing the reference to the managed object context anywhere in the model, it needs to be passed in when we want to create an object like this.

Okay so let’s implement the method in LogItem.swift:

class LogItem: NSManagedObject {

    @NSManaged var itemText: String
    @NSManaged var title: String

    class func createInManagedObjectContext(moc: NSManagedObjectContext, title: String, text: String) -> LogItem {
        let newItem = NSEntityDescription.insertNewObjectForEntityForName("LogItem", inManagedObjectContext: moc) as! LogItem
        newItem.title = title
        newItem.itemText = text
        
        return newItem
    }
    
}

The first line is the function definition. It’s a class function called createInManagedObjectContext, which takes an object of type NSManagedObjectContext as the argument moc, a String called title, and a String called text. The function returns a LogItem object that’s been inserted in to the specified managed object context.

Then it executes nearly identical code as before to create a new LogItem object, except now it’s using the arguments passed in to the function to set up the new LogItem object.

We can replace our original code in ViewController.swift now to just use the new method. Let’s add a bunch of new items…

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    // Use optional binding to confirm the managedObjectContext
    if let moc = self.managedObjectContext {
        
        // Create some dummy data to work with
        var items = [
            ("Best Animal", "Dog"),
            ("Best Language","Swift"),
            ("Worst Animal","Cthulu"),
            ("Worst Language","LOLCODE")
        ]
        
        // Loop through, creating items
        for (itemTitle, itemText) in items {
            // Create an individual item
            LogItem.createInManagedObjectContext(moc,
                title: itemTitle, text: itemText)
        }
    }
}

To keep the code simple, we’re using some shortcuts in order to seed our data. What you’re seeing when I’m setting items is an array (using square brackets []), then each element is a tuple of two String values, [(String, String)].

Next, I’m decomposing them back in to two variables, itemTitle and itemText for each of the tuples in the array.

Finally, I call the createInManagedObjectContext method, which we created earlier, passing in the new itemTitle and itemText.


If you already know how to set up a UITableView programmatically and want to skip ahead to the Core Data stuff, click here.

Now that we have a couple of records, let’s remove presentItemInfo and instead opt for a table view here. We’ll add this all right under viewDidLoad and programmatically create the UITableView. In my iTunes tutorial we do this using storyboards. If you are more interested in working with storyboards I recommend taking a pit stop there to read about how to get that set up.

We’ll set up the tableView by adding a logTableView to the ViewController class, and set it all up in viewDidLoad()

// Create the table view as soon as this class loads
var logTableView = UITableView(frame: CGRectZero, style: .Plain)

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    // Use optional binding to confirm the managedObjectContext
    if let moc = self.managedObjectContext {
        
        // Create some dummy data to work with
        var items = [
            ("Best Animal", "Dog"),
            ("Best Language","Swift"),
            ("Worst Animal","Cthulu"),
            ("Worst Language","LOLCODE")
        ]
        
        // Loop through, creating items
        for (itemTitle, itemText) in items {
            // Create an individual item
            LogItem.createInManagedObjectContext(moc,
                title: itemTitle, text: itemText)
        }
        
        
        // Now that the view loaded, we have a frame for the view, which will be (0,0,screen width, screen height)
        // This is a good size for the table view as well, so let's use that
        // The only adjust we'll make is to move it down by 20 pixels, and reduce the size by 20 pixels
        // in order to account for the status bar
        
        // Store the full frame in a temporary variable
        var viewFrame = self.view.frame
        
        // Adjust it down by 20 points
        viewFrame.origin.y += 20
        
        // Reduce the total height by 20 points
        viewFrame.size.height -= 20
        
        // Set the logTableview's frame to equal our temporary variable with the full size of the view
        // adjusted to account for the status bar height
        logTableView.frame = viewFrame
        
        // Add the table view to this view controller's view
        self.view.addSubview(logTableView)
        
        // Here, we tell the table view that we intend to use a cell we're going to call "LogCell"
        // This will be associated with the standard UITableViewCell class for now
        logTableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "LogCell")
        
        // This tells the table view that it should get it's data from this class, ViewController
        logTableView.dataSource = self
        
    }
}

Since we set the dataSource to be our ViewController class, we also need to adhere to the UITableViewDataSource protocol, so add that to the ViewController’s class definition:

class ViewController: UIViewController, UITableViewDataSource {

…and add the actual dataSource methods…

// MARK: UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 5
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("LogCell") as! UITableViewCell
    cell.textLabel?.text = "\(indexPath.row)"
    return cell
}

Still with me? Hope so… if not here’s the full ViewController.swift code up until this point. Note that we also removed the viewDidAppear function since we were only using that for testing out some things earlier.

import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDataSource {
    
    // Retreive the managedObjectContext from AppDelegate
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    // Create the table view as soon as this class loads
    var logTableView = UITableView(frame: CGRectZero, style: .Plain)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // Use optional binding to confirm the managedObjectContext
        if let moc = self.managedObjectContext {
            
            // Create some dummy data to work with
            var items = [
                ("Best Animal", "Dog"),
                ("Best Language","Swift"),
                ("Worst Animal","Cthulu"),
                ("Worst Language","LOLCODE")
            ]
            
            // Loop through, creating items
            for (itemTitle, itemText) in items {
                // Create an individual item
                LogItem.createInManagedObjectContext(moc,
                    title: itemTitle, text: itemText)
            }
            
            
            // Now that the view loaded, we have a frame for the view, which will be (0,0,screen width, screen height)
            // This is a good size for the table view as well, so let's use that
            // The only adjust we'll make is to move it down by 20 pixels, and reduce the size by 20 pixels
            // in order to account for the status bar
            
            // Store the full frame in a temporary variable
            var viewFrame = self.view.frame
            
            // Adjust it down by 20 points
            viewFrame.origin.y += 20
            
            // Reduce the total height by 20 points
            viewFrame.size.height -= 20
            
            // Set the logTableview's frame to equal our temporary variable with the full size of the view
            // adjusted to account for the status bar height
            logTableView.frame = viewFrame
            
            // Add the table view to this view controller's view
            self.view.addSubview(logTableView)
            
            // Here, we tell the table view that we intend to use a cell we're going to call "LogCell"
            // This will be associated with the standard UITableViewCell class for now
            logTableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "LogCell")
            
            // This tells the table view that it should get it's data from this class, ViewController
            logTableView.dataSource = self
            
        }
    }
    
    // MARK: UITableViewDataSource
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("LogCell") as! UITableViewCell
        cell.textLabel?.text = "\(indexPath.row)"
        return cell
    }

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

}

This will give us a numbered list of rows if we run the app. This just confirms the table view is set up correctly.

If you ever get an error that says something like, The model used to open the store is incompatible with the one used to create the store
This means that the model Core Data thinks it’s working with doesn’t quite match what’s been stored in the database. Resolving this with a live app means migrating between versions of your model. But for the purpose of learning and doing the initial development, usually the quickest way to just keep moving is to delete the app from the simulator; wiping out the data, and therefore resolving the conflict.

Now that we have this all set up and ready to go, we can start working on getting our log data showing up in the table view.

Please note that at this point in the tutorial, I’m intentionally avoiding the NSFetchedResultsController class. I’m doing this because I believe it will make more sense from the Core Data perspective the first time around if you see it done the old fashioned way. After this tutorial is over, I encourage you to look over the class and see how you might use it to implement some of these things instead. I think it is important to first learn how to implement a core data backed table view without using the helper class. The helper class is not applicable in all situations, and learning it first would be doing yourself a disservice. You’ll find it doesn’t work for all use cases (not even close), and in it’s magic it hides some of what’s going on.

First off, let’s fetch all the results from Core Data in viewDidLoad(), and store them in an array of LogItem’s, logItems.

First we’ll add the variable to the ViewController class:

// Create an empty array of LogItem's
var logItems = [LogItem]()

Next, we’ll populate it from viewDidLoad(), inside of a function called fetchLog().

override func viewDidLoad() {
    ...
    fetchLog()
}

func fetchLog() {
    let fetchRequest = NSFetchRequest(entityName: "LogItem")
    if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LogItem] {
        logItems = fetchResults
    }
}

Now we can modify the tableView dataSource methods to refer to this array instead of hard-coding values.

// MARK: UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // How many rows are there in this section?
    // There's only 1 section, and it has a number of rows
    // equal to the number of logItems, so return the count
    return logItems.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("LogCell") as! UITableViewCell
    
    // Get the LogItem for this index
    let logItem = logItems[indexPath.row]
    
    // Set the title of the cell to be the title of the logItem
    cell.textLabel?.text = logItem.title
    return cell
}

This should set us up to see the items listed in the table view, but we want to show the text of an item when it’s clicked. So we need to set the table view to use this view controller as it’s delegate, so we can receive the callback method, didSelectRowAtIndexPath.

Similar to before, add the UITableViewDelegate protocol to the class…

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

And set the delegate to be self, you can do this right under where you set the dataSource in viewDidLoad…

// This tells the table view that it should get it's data from this class, ViewController
logTableView.dataSource = self
logTableView.delegate = self

Finally we can implement the method, knowing the Table View will call it when a cell is clicked…

// MARK: UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let logItem = logItems[indexPath.row]
    println(logItem.itemText)
}

This will set us up so that when the button is clicked, a message will show in the console (in the Xcode window) showing the itemText for the clicked item.

 

The purpose of this tutorial is not really to explain how to set up a table view manually, but it’s sort of necessary in order to get a good look at the data. For this reason, I’m providing the source code up until this point in the tutorial here. Feel free to simply check this out and work from this code base, from here we’ll be talking more about the Core Data side of things.
https://github.com/jquave/Core-Data-In-Swift-Tutorial/tree/Part1.5

You may see something kind of like this now, except with different data:

Your ordering may be different every time you run your app, this is because it contains no sorting when it performs the fetch request. In some data stores you might get this data back in the same order as it was inserted, but with Core Data it ends up being fairly random. Let’s change this by adding a sort to the fetch request.

func fetchLog() {
    let fetchRequest = NSFetchRequest(entityName: "LogItem")
    
    // Create a sort descriptor object that sorts on the "title"
    // property of the Core Data object
    let sortDescriptor = NSSortDescriptor(key: "title", ascending: true)
    
    // Set the list of sort descriptors in the fetch request,
    // so it includes the sort descriptor
    fetchRequest.sortDescriptors = [sortDescriptor]
    
    if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LogItem] {
        logItems = fetchResults
    }
}

Now the fetch request has it’s sortDescriptors property set. Note that this is an array, which is why we need the brackets around the single sortDescriptor we created using the title key. Running the app you should now see the sorted (alphabetically) list of items, much better. Note your data is expected to be different.

Let’s also look at filtering out certain elements. First, let’s just try and only get the item named “Best Language”. We’ll create an NSPredicate that uses a string to represent the requirement that any object must fulfill in order to pass through the query.

func fetchLog() {
    let fetchRequest = NSFetchRequest(entityName: "LogItem")
    
    // Create a sort descriptor object that sorts on the "title"
    // property of the Core Data object
    let sortDescriptor = NSSortDescriptor(key: "title", ascending: true)
    
    // Set the list of sort descriptors in the fetch request,
    // so it includes the sort descriptor
    fetchRequest.sortDescriptors = [sortDescriptor]
    
    // Create a new predicate that filters out any object that
    // doesn't have a title of "Best Language" exactly.
    let predicate = NSPredicate(format: "title == %@", "Best Language")
    
    // Set the predicate on the fetch request
    fetchRequest.predicate = predicate
    
    if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LogItem] {
        logItems = fetchResults
    }
}

If you haven’t seen the format syntax yet, or haven’t seen it in a while, it’s simple enough to say that any time you see format as a named parameter, it comes from the Objective-C methods “predicateWithFormat”, or “stringWithFormat”, and so on. This replaces any instances of the %@ symbol with an object’s description (the value of a string, or an otherwise useful representation of other types of objects). For primitive types such as an Int, you’ll want to instead opt for %i, %f for Float, and so on.

So when you see
(format: “title == %@”, “Best Language”)

What the compiler sees is something like this:
(“title == ‘Best Language'”)

So we’re just specifying we want title to equal that exact string.

Running the app now we should see just one item.

We could also do a string comparison using the contains keyword, if we look for the substring “Worst” we’ll only get the items that contain that string…

// Search for only items using the substring "Worst"
let predicate = NSPredicate(format: "title contains %@", "Worst")

// Set the predicate on the fetch request
fetchRequest.predicate = predicate

What if we wanted to combine the two though? We want both items containing the string “Worst” and any one with a title “Best Language”?

First, let’s create the two separate predicates:

// Create a new predicate that filters out any object that
// doesn't have a title of "Best Language" exactly.
let firstPredicate = NSPredicate(format: "title == %@", "Best Language")

// Search for only items using the substring "Worst"
let thPredicate = NSPredicate(format: "title contains %@", "Worst")

Then combine them using the NSCompoundPredicate constructor:

// Combine the two predicates above in to one compound predicate
let predicate = NSCompoundPredicate(type: NSCompoundPredicateType.OrPredicateType, subpredicates: [firstPredicate, thPredicate])

// Set the predicate on the fetch request
fetchRequest.predicate = predicate

Since we want both cases of the “Best Language” and any item containing “Worst”, we use a compound predicate type of NSCompoundPredicateType.OrPredicateType

All this is just a confusing way of saying, “give me any items where the firstPredicate or the thPredicate is true.”

What we did here is quite powerful in practice. We can use string comparison as predicates, and filter/order large lists of data through Core Data. The Core Data API will then connect to the backing store (SQLite) and produce an efficient query to quickly retrieve the needed information. This is a very common pattern in iOS development, and understanding it is very important. So, if you got stuck on this tutorial or have any questions, don’t hesitate to ask for help on the forums.

This concludes Part 2 for now, in the next section we’ll move in to a more applicable scenario by adding a way for user’s to create log items, edit, save, and delete them.

The complete source code to this section »

If you’re reading this scratching your head, or just want a deeper dive in to Core Data, be sure to look at my Swift book, which is now available for early access. And don’t forget to subscribe to get updates on this tutorial series via e-mail.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

Core Data in Swift Tutorial (Part 1)

This post compatible with Xcode 6.3 Beta, Updated on February 16, 2015
Don’t have 6.3 yet? Make sure to download it here using your iOS Developer account.

Core Data is the de facto standard way to persist and manage data in both iPhone and Mac applications, and with Swift it’s a bit easier. So it’s only natural that we should take some time to learn about it when building apps. Eager to see what we’ll have created by the end of this tutorial? Take a look at the video, we’ll be creating this table view, populating it with data, adding the ability to delete records, add records, and sort/search records all backed by Core Data. This data is persistent and lasts even through a complete shut down of your phone.

The first thing to know about Core Data before diving in is that it is not a relational database, and although it uses SQLite as a backing engine, is not an ORM to a relational database either. The SQLite backend is more of an implementation detail, and in fact binary files or plists can be used instead.

The official Apple documentation describes Core Data like this:

“The Core Data framework provides generalized and automated solutions to common tasks associated with object life-cycle and object graph management, including persistence.”
[developer.apple.com]

Before we get too technical about what Core Data is, I think it’s useful to dive in and start playing with the API a bit.

Create a new Xcode 6 project using a single-view template, Swift as the language, and with Core Data enabled. I’ll call the project MyLog.


Looking for something more in-depth than a tutorial? Try my book & video courses
Get The Swift Book
Learn About My Book & Video Packages »

Looking at the AppDelegate.swift file you’ll notice using this option has added quite a few functions. Most of these are setting up the Core Data stack. The defaults are fine for now. The primary object that needs to be used to work with Core Data is the managedObjectContext defined here.

If you used the Core Data template as shown above, this code will already be present.

lazy var managedObjectContext: NSManagedObjectContext? = {
    // Returns the managed object context for the application (which is already bound to the persistent store
    // coordinator for the application.) This property is optional since there are legitimate error
    // conditions that could cause the creation of the context to fail.
    let coordinator = self.persistentStoreCoordinator
    if coordinator == nil {
        return nil
    }
    var managedObjectContext = NSManagedObjectContext()
    managedObjectContext.persistentStoreCoordinator = coordinator
    return managedObjectContext
}()

All you really need to know about this, is that managedObjectContext is a lazy variable on AppDelegate that is at our disposable for use in performing Core Data calls. Knowing this we can access the managedObjectContext from our ViewController.swift file. For example in viewDidLoad() of ViewController.swift, we can use this code to print the managedObjectContext’s description to the console. (New lines are highlighted)

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    // Retreive the managedObjectContext from AppDelegate
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
    
    // Print it to the console
    println(managedObjectContext)
}

We’ll be accessing the managedObjectContext pretty frequently, so we should pull this out of the viewDidLoad() method and move it somewhere we can access it easily. How about if we just store it as an instance variable on the ViewController?

import UIKit

class ViewController: UIViewController {
    
    // Retreive the managedObjectContext from AppDelegate
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // Print it to the console
        println(managedObjectContext)
    }

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

}

The managedObjectContext variable is computed using the existing managedObjectContext in the application’s delegate. In viewdidLoad() we cause this variable to be computed by printing it to the console. If your application is set up right you should see something like this:

Optional(<NSManagedObjectContext: 0x7fe68b58c800>)

You’ll notice the Xcode template produced an additional file, MyLog.xcdatamodeld.

Opening up this file you can see the Core Data model editor.

Let’s add a new Core Data entity called LogItem. Our log app will show a list of LogItems, which have a bit of text in them.

Click the “Add Entity” button and then in the right-hand panel select the “Data Model Inspector”. From here we can rename the default name, Entity, to LogItem.

Next, at the bottom we can add our first attribute by pressing the “+ Atrribute” button at the bottom.

Name this attribute title, and give it a type of String. We’ll also add a second attribute of type String called itemText.

IMPORTANT!

From this point on, any changes you make to your Core Data model, such as adding a new Entity or Attribute will lead to an inconsistency in the model of the app in the iPhone Simulator. If this happens to you, you’ll get a really scary looking crash in your app as soon as it starts. You’ll also see something like this show up at the very bottom of your console, “reason=The model used to open the store is incompatible with the one used to create the store”.

If this happens to you there is a very easy fix:
In the iPhone Simulator, or on your device, just delete the app, and then perform a new Build & Run command in Xcode. This will erase all out of date versions of the model, and allow you to do a fresh run.

Now that we have our first Entity created, we want to also be able to directly access this entity as a class in our code. Xcode provides an automated tool to do this. In the menubar select Editor->Create NSManagedObject Subclass…

In the first prompt, check the MyLog model and press next. Then, check the LogItem entity, and press next again.
A file save window should appear with an option to specify the language as Swift, select this. Finally hit Create, and you should now see a LogItem.swift file added. It’s contents should be something very close to this:

import Foundation
import CoreData

class LogItem: NSManagedObject {
    @NSManaged var title: String
    @NSManaged var itemText: String
}

This class is generated from the xcdatamodeld file. The entity we created is represented by the similarly named class LogItem, and the attributes are turned in to variables using the @NSManaged identifier, which gives the variables special treatment allowing them to operate with Core Data. For most intents and purposes though, you can just think of these as instance variables.

Because of the way Swift modules work, we need to make one modification to the core data model. In the field “Class” under the data model inspector for our entity, LogItem, we need to specify the project name as a prefix to the class name. So instead of just specifying “LogItem” as the class, it needs to say “MyLog.LogItem”, assuming your app is called “MyLog”.

In our ViewController.swift file in the viewDidLoad method, let’s instantiate some instances of LogItem. There are many ways to do this, but the least verbose is to use the insertNewObjectForEntityForName method of NSEntityDescription.

override func viewDidLoad() {
    super.viewDidLoad()

    let newItem = NSEntityDescription.insertNewObjectForEntityForName("LogItem", inManagedObjectContext: self.managedObjectContext!) as! LogItem
}

Here we insert a new object in to the core data stack through the managedObjectContext that the template function added to AppDelegate for us. This method returns an NSManagedObject which is a generic type of Core Data object that responds to methods like valueForKey. If you don’t quite understand what that means, don’t worry too much about it, it’s not going to prevent you from being able to use Core Data. Let’s keep moving.

With an NSManagedObject version of newItem, we could say newItem.valueForKey(“title”) to get the title. But this is not the best approach because it leaves too much opportunity to mis-type an attribute name, or get the wrong object type unexpectedly and have hard crashes trying to access these attributes.

So, in our case, we instead cast the NSManagedObject that insertNewObjectForEntityForName returns, to our generated class LogItem.

What this means, simply put, is that we can set the title and itemText like this:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    let newItem = NSEntityDescription.insertNewObjectForEntityForName("LogItem", inManagedObjectContext: self.managedObjectContext!) as! LogItem

    newItem.title = "Wrote Core Data Tutorial"
    newItem.itemText = "Wrote and post a tutorial on the basics of Core Data to blog."
}

If we had not generated our LogItem.swift file earlier, the type LogItem would not be defined and we would be limited to working only with NSManagedObject types. This is a really nasty way to work with the Core Data API as it relies heavily on determining object classes, entities, state, and more at runtime based on string comparisons, yuck!

Now that we’ve created a new item, and set both it’s title and text, we can query it elsewhere in our app and get the object back. Let’s override the viewDidAppear() method and implement a way to look at our items info. We’ll perform a Core Data fetch (which is like a query if you have worked with SQL before), then we will present the contents of the row we retrieved in a new alert window.

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    
    // Create a new fetch request using the LogItem entity
    let fetchRequest = NSFetchRequest(entityName: "LogItem")
    
    // Execute the fetch request, and cast the results to an array of LogItem objects
    if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LogItem] {
        
        // Create an Alert, and set it's message to whatever the itemText is
        let alert = UIAlertController(title: fetchResults[0].title,
            message: fetchResults[0].itemText,
            preferredStyle: .Alert)
        
        // Display the alert
        self.presentViewController(alert,
            animated: true,
            completion: nil)
    }
}

First, we create a new NSFetchRequest instance using the entity LogItem.
Next, we create a fetchResults variable by using the executeFetchRequest method of managedObjectContext. The only thing we have specified in our fetchRequest is the entity, so this particular fetch just returns every record. If you are familiar with SQL, a fetch request with no predicate on the LogItem entity is something like “SELECT * FROM LogItem”.
Next we create a UIAlertController instance to present a message to the screen, and set it’s title and message properties to the title and itemText properties of the first LogItem in our fetch results (which is an array of LogItem objects).

Run the app and you should see the item presented to the screen. You’ve now stored and retrieved data from Core Data. This is the first step in to building apps with persistent storage.

In part 2, we’ll talk about working with multiple records and using NSPredicate to perform filtered requests.

The full source code to this part is available here on Github.

If you found this post useful, check out my other tutorials, and take a look at my Swift eBook, which is now available for early access.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

Taking control of the iPhone camera in iOS 8 with Swift (Part 1)

Updated on September 20, 2014 for Xcode 6 GM

Using the AVFoundation API, we are going to set up a capture session and make an app that allows us to use all the new fine-grained controls added to iOS 8. This includes manually controlling focus, exposure, and ISO. First off, we just need to set up a basic camera preview. By the end of Part 1 we’ll have that in place along with a nifty way to control focus. This tutorial is adapted from the more detailed project PhotoSwapr in my book. Ready? Let’s get going…


Looking for something more in-depth than a tutorial? Try my book & video courses
Get The Swift Book
Learn About My Book & Video Packages »

First off we’ll create a new Xcode Project using Swift as the language, and using the Single View template.

singleAppTemplate

Now, in the ViewController.swift file we can start adding in our custom code inside of viewDidLoad().

First we create a AVCaptureSession object to work with. Let’s do this as a class variable.

let captureSession = AVCaptureSession()

This may give an error due to not being able to find AVCaptureSession. So near the top of the file make sure to add:

import AVFoundation

Now, in viewDidLoad let’s set our quality settings and find a device to record from.

First, let’s take a look at the list of devices available.

captureSession.sessionPreset = AVCaptureSessionPresetLow
let devices = AVCaptureDevice.devices()
println(devices)

Run this and you’ll see something like this:

[<AVCaptureFigVideoDevice: 0x16e7f720 [Back Camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>,
<AVCaptureFigVideoDevice: 0x16d91a00 [Front Camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>,
<AVCaptureFigAudioDevice: 0x16e88c00 [iPhone Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>]
pple.avfoundation.avcapturedevice.built-in_video:1]>,
<AVCaptureFigAudioDevice: 0x16e88c00 [iPhone Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>]

This is from my iPhone 5S. Looks like we have two microphones, and the front and back cameras. Cool. For our purposes let’s try and grab the back camera.

Let’s add this to a ViewController, and store the front facing camera if we find one

import UIKit
import AVFoundation

class ViewController: UIViewController {

    let captureSession = AVCaptureSession()

    // If we find a device we'll store it here for later use
    var captureDevice : AVCaptureDevice?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        captureSession.sessionPreset = AVCaptureSessionPresetLow

        let devices = AVCaptureDevice.devices()

        // Loop through all the capture devices on this phone
        for device in devices {
            // Make sure this particular device supports video
            if (device.hasMediaType(AVMediaTypeVideo)) {
                // Finally check the position and confirm we've got the back camera
                if(device.position == AVCaptureDevicePosition.Back) {
                    captureDevice = device as? AVCaptureDevice
                }
            }
        }

    }

}

After we set the captureDevice, let’s begin the session by implementing a function to start the session

if captureDevice != nil {
    beginSession()
}

…and later in the class we implement beginSession()…

func beginSession() {
    var err : NSError? = nil
    captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))

    if err != nil {
        println("error: \(err?.localizedDescription)")
    }

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    self.view.layer.addSublayer(previewLayer)
    previewLayer?.frame = self.view.layer.frame
    captureSession.startRunning()
}

If you run the app on a device now, you should see a preview of the camera. This is pretty much just the standard iOS camera. Let’s now modify the focus mode. Add a new method called configureDevice() and have beginSession() call it on the first line starting the capture session.

func configureDevice() {
    if let device = captureDevice {
        device.lockForConfiguration(nil)
        device.focusMode = .Locked
        device.unlockForConfiguration()
    }
}

Add this method, it locks the device, sets the focus to locked, and then unlocks the device.

Run the app now and try tapping to focus on different parts of the scene. The default focus behavior should now be disabled. This means we can control the focus on our own. Let’s add a UISlider to control the focus.

Now, let’s add a manual focusTo function based on a value from 0.0 to 1.0

func focusTo(value : Float) {
    if let device = captureDevice {
        if(device.lockForConfiguration(nil)) {
            device.setFocusModeLockedWithLensPosition(value, completionHandler: { (time) -> Void in
                //
            })
            device.unlockForConfiguration()
        }
    }
}

First, we validate that the device exists, then we lock the device. If the lock is successful we call the setFocusModeLockedWithLensPosition() API to tell the lens to focus on the point ‘value’, which is passed in to the focusTo() method.

Now let’s implement touch controls using these methods:

let screenWidth = UIScreen.mainScreen().bounds.size.width
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    var anyTouch = touches.anyObject() as UITouch
    var touchPercent = anyTouch.locationInView(self.view).x / screenWidth
    focusTo(Float(touchPercent))
}

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
    var anyTouch = touches.anyObject() as UITouch
    var touchPercent = anyTouch.locationInView(self.view).x / screenWidth
    focusTo(Float(touchPercent))
}

This just gets a value from 0.0 to 1.0 based on how far you are touching on the screen horizontally. Run the app now and slide a finger left to right on the screen. You can now manually control focus this way! Cool right?

Next time we’ll add an option for manually setting the ISO and exposure. But for now, this is a start. Make sure to subscribe to my newsletter to be notified of Part 2. Coming soon!

Want a deeper look at the AVFoundation API? Pre-order my upcoming book on developing iOS 8 Apps in Swift.

Here is the final code from this post:

Part 1 on Github

import UIKit
import AVFoundation

class ViewController: UIViewController {

    let captureSession = AVCaptureSession()
    var previewLayer : AVCaptureVideoPreviewLayer?

    // If we find a device we'll store it here for later use
    var captureDevice : AVCaptureDevice?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.
        captureSession.sessionPreset = AVCaptureSessionPresetHigh

        let devices = AVCaptureDevice.devices()

        // Loop through all the capture devices on this phone
        for device in devices {
            // Make sure this particular device supports video
            if (device.hasMediaType(AVMediaTypeVideo)) {
                // Finally check the position and confirm we've got the back camera
                if(device.position == AVCaptureDevicePosition.Back) {
                    captureDevice = device as? AVCaptureDevice
                    if captureDevice != nil {
                        println("Capture device found")
                        beginSession()
                    }
                }
            }
        }

    }

    func focusTo(value : Float) {
        if let device = captureDevice {
            if(device.lockForConfiguration(nil)) {
                device.setFocusModeLockedWithLensPosition(value, completionHandler: { (time) -> Void in
                    //
                })
                device.unlockForConfiguration()
            }
        }
    }

    let screenWidth = UIScreen.mainScreen().bounds.size.width
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        var anyTouch = touches.anyObject() as UITouch
        var touchPercent = anyTouch.locationInView(self.view).x / screenWidth
        focusTo(Float(touchPercent))
    }

    override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
        var anyTouch = touches.anyObject() as UITouch
        var touchPercent = anyTouch.locationInView(self.view).x / screenWidth
        focusTo(Float(touchPercent))
    }

    func configureDevice() {
        if let device = captureDevice {
            device.lockForConfiguration(nil)
            device.focusMode = .Locked
            device.unlockForConfiguration()
        }

    }

    func beginSession() {

        configureDevice()

        var err : NSError? = nil
        captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))

        if err != nil {
            println("error: \(err?.localizedDescription)")
        }

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        self.view.layer.addSublayer(previewLayer)
        previewLayer?.frame = self.view.layer.frame
        captureSession.startRunning()
    }

}


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

Using Equatable and NilLiteralConvertible to re-implement Optionals in Swift (Part 2)

This post updated December 6, 2014 to reflect changes in Xcode 6.2

A few weeks ago I did a little thought experiment on how we might re-implement Swift’s optional type by using the Swift enum. If you haven’t read that yet, you can read it here. In this post, we’re going to push a little further and see if we can get something that corresponds a little more closely to the Swift Optional type.

In the last post we ended up with this class, JOptional:

enum JOptional<T> {
    case None
    case Some(T)
    
    init(_ value: T) {
        self = .Some(value)
    }
    
    init() {
        self = .None
    }
    
    func unwrap() -> Any {
        switch self {
        case .Some(let x):
            return x
        default:
            fatalError("Unexpectedly found nil while unwrapping an JOptional value")
        }
    }
    
}

…and a custom operator…

postfix operator >! {}
postfix func >! <T>(value: JOptional<T> ) -> Any {
    return value.unwrap()
}

It’s fairly straightforward to use the class by using the generic constructor, passing in your value, and then using our overloaded >! operator to unwrap.

// Instantiate some optionals
var myOptionalString = JOptional("A String!")
var a = myOptionalString.unwrap() // "A String!"
var c = myOptionalString>!     // "A String!"

In Swift’s actual Optional implementation, as of Xcode 6 Beta 5, Optionals can also be compared to nil, without doing any unwrapping first. For example:

var myNilValue : String? = nil

if(myNilValue == nil) {
    println("This is nil!")
}
else {
    println("This isn't nil, carry on")
}
This is nil!

This simply creates your standard Swift optional set to a nil value, then we can compare directly to nil and act upon this information. If we try this same code with our version of optional, we run in to a problem:

var myNilJOptional = JOptional(myNilValue)

if(myNilJOptional == nil) {
    println("This is nil!")
}
else {
    println("This isn't nil, carry on")
}
This isn't nil, carry on

Or even worse, you might get an error about Optional<String?> not conforming to MirrorDisposition, this is basically Swift being unable to find comparison operators between Optional and nil… so it’s falling back to what it can find: MirrorDisposition, which is something Swift uses for some of the nifty IDE features.

So, what’s going on here?

Let’s dig a little deeper… If we comment out our comparisons and just put a breakpoint right after the setting of the myNilJOptional value, and check them out in the watch window, we see the following:

The JOptional is nil, but it has a ‘Some’ case instead of ‘None’, because we used the init(_ T) method to instantiate it, and the init method sets the Some case even if value is nil. As a result, JOptional is in fact *not* equal to nil!

We could modify this behavior by doing a check on that init method and setting None if the object is nil, but this introduces the issue of needing to update the case if the inner object is modified. This introduces a variety of issues, so what may be better is to simply implement two Swift interfaces that help solve this common problem: Equatable and NilLiteralConvertible.

Equatable just specifies that we are going to implement our own operator for ==, specific to our type.
NilLiteralConvertible gives an interface to convert between JOptionals and nil literals. In our case, a nil object with type JOptional should be converted to JOptional.None.

So first, we add the interfaces to our JOptional definition:

enum JOptional<T> : Equatable, NilLiteralConvertible {
  ...

Next, we need to implement the equality operator. Because this is an operator overload, it goes in global space (outside of the enum):

func == <T>(lhs: JOptional<T>, rhs: JOptional<T>) -> Bool {
    switch (lhs,rhs) {
    case (.Some(let lhsVal), .Some(let rhsVal)):
        // Both have a value, the *optionals* are equal, although the values might not be
        return true
    case (.None, .None):
        // Both are nil
        return true
    default:
        // One does not have a value, but the other does
        return false
    }
}

We’re using pattern matching of tuples here in order to handle three cases:

  1. Both values are not-nil
  2. Both values are nil
  3. Values nil-ness does not match

It’s important to note, comparing two optional (even in Swift’s implementation) only compares that the case is the same (Some vs None) It does not compare the inner values, you need to unwrap for that behavior.

If we get a nil lhs or rhs value in this == operator, we need Swift to know to convert that to the .None case. So we implement the init(nilLiteral: ()) method.

init(nilLiteral: ()) {
    self = None
}

So now, not only can we say things like this:

myNilJOptional = nil

And the result will be that myNilJOptional is set to JOptional.None

At the same time, we can do direct comparisons of JOptional values due to the equatable method, including to nil values. Like this:

if(myNilJOptional == nil) {
  // Do something
  println("This JOptional is nil!")
}

Checking out earlier code again, we get the expected result:

This is nil!
This is nil!

Full source code of Part 2 found here &raqou;

This topic is explored more in-depth in my upcoming book.

In the meantime, take a look at my other tutorials and posts about Swift.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

Re-implementing Optionals using Swift’s powerful enum type.

This post updated December 6, 2014 to reflect changes in Xcode 6.2

If you’ve been messing with Swift much lately, you may have noticed that enum’s do much more than they did in Objective-C, or in most other languages for that matter. In the WWDC videos discussing Swift, one of the presenters mentions that Optionals are implemented as enums. Let’s explore this a bit and see if we could possibly implement Optionals ourselves, as an exercise in exploring Swift’s enum type.

The first thing to note about an Apple’s implementation of Optional is that it’s an enum with two cases, Some and None. So first off, we create an enum, let’s call it JOptional, and add these cases.

enum JOptional {
    case None
    case Some
}

When we set the enum case to ‘Some’, we should also have an associated value, but it can be any object type. So we use the T keyword to specify it’s a generic type.

enum JOptional<T> {
    case None
    case Some(T)
}

We haven’t built out any fancy shortcuts that Swift’s regular optionals use yet, but we can still instantiate JOptionals directly if we add some init() methods.

enum JOptional<T> {
    case None
    case Some(T)
    
    init(_ value: T) {
        self = .Some(value)
    }
    
    init() {
        self = .None
    }
    
}

// Instantiate some optionals
var myOptionalString = JOptional("A String!")
var myNilString = JOptional()

Already we have a bit of a shortcut baked in here. The type of myOptionalString is JOptional<String>, but because we added a init(_ value: T) method to the enum, we can pass in:

  1. An unnamed parameter, due to the use of the _ for external name
  2. Of any type, since it is accepting a generic type of value T
  3. That initializes self, and sets the case to .Some with the attached value

Taking a look at the value of myOptionalString (I’m using a playground for this) you should see (Enum value). This is similar to how Swift’s optionals are “wrapped”. So, we need an unwrap method. Here is what I came up with:

func unwrap() -> Any {
    switch self {
    case .Some(let x):
        return x
    default:
        assert(true, "Unexpectedly found nil while unwrapping an JOptional value")
    }
    return JOptional.None
}

This method uses the value-binding feature of Swift to determine if the enum value can be matched against .Some(let x):
If it can, then it’s safe to assume that x actually contains something, and since this is an unwrapping, we can return it.

Otherwise, this falls back to the default case, which is where we need to point out to the developer that they attempted to unwrap a JOptional that wasn’t able to be bound to x. I added an assert here, I believe this is also present in Apple’s Optional enum, since a force unwrapping of an Optional produces a hard crash.

We can test this out, like so:

var myOptionalString = JOptional("A String!")
var a = myOptionalString.unwrap()

The value of a should now be to “A String!”, while myOptionalString should still be JOptional.Some(“A String!”), which will print as (Enum value).

In Apple’s Optional, one can unwrap by just appending ! to the end of the variable. We can’t use this symbol since it’s reserved, but there’s no reason we can add >! as a new postfix operator. Why >! you ask? I have no idea, but it’s not taken. Actually, this is a pretty good example of how to not choose operator overloads, but that’s okay. No one is ever going to use this, I hope.

On line 1 we create the postfix, then on line 2 we implement the overload to the operator

postfix operator >! {}
postfix func >! <T>(value: JOptional<T> ) -> Any {
    return value.unwrap()
}

Now, we can unwrap the optional with our custom operator

// Instantiate some optionals
var myOptionalString = JOptional("A String!")
var a = myOptionalString.unwrap() // "A String!"
var c = myOptionalString>!     // "A String!"

Full code for this part here »

This concludes part 1. In the next part we’ll implement nil comparison using the Equatable and NilLiteralConvertible interfaces.
Go To Part 2 »

This topic is also explored more in-depth in my upcoming book.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS

Swift: “fatal error: unexpectedly found nil while unwrapping an Optional value” errors in Swift

fatal error: unexpectedly found nil while unwrapping an Optional value

Have you seen this yet working with Swift yet? If so, you’re not alone. In this post I’m going to dig in to what this means, and explain a bit about optionals in Swift. For those of you who are experienced programmers and have arrived at this page trying to figure out what the error means, here’s the short version:

It means your variable is set to nil, but your code is expecting it to not be nil.

To help understand optionals, let’s open up a Swift REPL. Open up Terminal and boot up the REPL:

swift

Let’s get this error to show up, and analyze what’s happening.
First, create an optional with nothing set:

var username: String?
username: String? = nil

From the REPL it’s clear that username is set to nil, in Xcode it’s not always as obvious. Let’s go ahead and try to use the variable:

print(username)
nil

Okay, confirmed. username is nil. print() understands this and just tells us as much. But, what if we want to make it uppercase using username.uppercaseString? First, let’s try it with a regular ol’ string.


"Jameson".uppercaseString

String = “JAMESON”

Cool, it works. Now let’s try it on username:


username.uppercaseString

error: value of optional type ‘String?’ not unwrapped; did you mean to use ‘!’ or ‘?’?

Oops, we can’t use uppercaseString because it’s not available in the Optional String?
We need to get an “unwrapped” String variable in order to use uppercaseString, because otherwise what we’re seeing is an optional value. Think of this like a “box” we keep values inside of. The box may be empty, or it may contain the value we want. But to be sure, and to work with the internals, we need to force unwrap it by adding an exclamation mark to the end.


username!.uppercaseString

fatal error: unexpectedly found nil while unwrapping an Optional value

Because we tried to force unwrap the optional without ever setting a value to it, we get this error. So what is username? We never set it! This is what the optional type protects from (ourselves)

Optional is actually an enum that contains some information on the variable that’s “inside” it.

When it contains something, the case is ‘Some’, and when it’s empty it’s value is ‘None’. Or in other words.. the Swift compiler is asked to work with what’s inside the box, and if it’s empty, it’s case is “None”. Otherwise, it’s case is “Some” with an associated value, like a username.

So what should you do about it? It’s pretty simple, just use optional binding. This unwraps an optional safely and allows us to execute a block of code *only* if the optional actually contained a value. It looks like this:


if let username = username {
print(username)
}

If you type this in to the REPL, you’ll see nothing happen. Why? Well, because username is “None”! It contains nothing. Try it again with a value set:


let username: String? = "jquave"
if let usernameUnwrapped = username {
print(usernameUnwrapped)
}

jquave

So there you go. Just use optional binding! If you want to unwrap a bunch of variables before proceeding, you can chain these bindings together with commas. So for example if we had to get both a username and password it would look like this:


let username: String? = "jquave"
let password: String? = "romneycomeback"
if let usernameUnwrapped = username, let passwordUnwrapped = password {
print(usernameUnwrapped)
print(passwordUnwrapped)
}

If you’re interested in learning more about Swift, be sure to subscribe to my mailing list for updates. I also recently released a complete Swift 3 Fundamentals Tutorial that covers the language in greater detail.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

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 ->


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

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 1, Part 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 ->


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

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 ->


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS