Core Data in Swift Tutorial (Part 3)

This post compatible with Xcode 6.3 Beta, Updated on February 16, 2014

This is part three 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.


In this (somewhat lengthy) section of the tutorial, we’ll implement deleting rows, adding rows, and persisting the data so it stays put even after app close. When we’re done we’ll have something that works like the video here:

Implementing swipe-to-delete

Before we get started, let’s go ahead and wipe out the filtering we were toying around with in fetchLog(). We’ll just simplify fetchLog to look like this:

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

First, we just need to implement the canEditRowAtIndexPath callback from the UITableViewDataSource protocol. In our case, we’ll be able to delete anything, so we can just return true all the time. But, if for example we had some entries locked, we could check the indexPath and return false instead.

func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

Now, this tells the UITableView that we can edit these rows (deleting a row is a form of editing it.) But iOS will look for one more callback, tableView:commitEditingStyle:forRowAtIndexPath.

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if(editingStyle == .Delete ) {
        // Find the LogItem object the user is trying to delete
        let logItemToDelete = logItems[indexPath.row]
        
        // Delete it from the managedObjectContext
        managedObjectContext?.deleteObject(logItemToDelete)
        
        // Refresh the table view to indicate that it's deleted
        self.fetchLog()
        
        // Tell the table view to animate out that row
        logTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
}

This method gets called on the UITableViewDataSource as well, and it’s called upon performing the swipe-to-delete action on iOS. Without this method implemented swipe-to-delete will simply be disabled.

So, we add it in here and we take action based on the deleted row. When the editingStyle is .Delete, we know that the intention is the delete the row. We identify the row data from our logItems array by accessing the indexPath.row index (this is the integer row number of the deleted item in the table view)

Next, we delete the object in Core Data using the deleteObject method on the managedObjectContext we computed earlier.

Now, we can update the table view itself by calling self.fetchLog() again. We could stop here and called reloadData() on the tableview, but instead we can use the deleteRowsAtIndexPaths:withRowAnimation: method of the table view to get some free table view animations.

Try running the app now, and you can swipe left on a table view row to delete it. You’ll get some free animations, and this data is updated in our Core Data object graph.

Being able to delete objects is great, but we also need to be able to add items if this is going to be a real app. Let’s make an adjustment to our viewDidLoad() code.

We have a line in our code right now setting the height of the tableview:

// Reduce the total height by 20 points
viewFrame.size.height -= 20

If we’re going to add a button we’ll just to adjust this even further, by the amount of the button height, and create the button. So we’ll set that up now:

// Add in the "+" button at the bottom
let addButton = UIButton(frame: CGRectMake(0, UIScreen.mainScreen().bounds.size.height - 44, UIScreen.mainScreen().bounds.size.width, 44))
addButton.setTitle("+", forState: .Normal)
addButton.backgroundColor = UIColor(red: 0.5, green: 0.9, blue: 0.5, alpha: 1.0)
addButton.addTarget(self, action: "addNewItem", forControlEvents: .TouchUpInside)
self.view.addSubview(addButton)

// Reduce the total height by 20 points for the status bar, and 44 points for the bottom button
viewFrame.size.height -= (20 + addButton.frame.size.height)

First, we create a UIButton instance, and set it’s size to be equal to 44×44 points. The position will be at an x coordinate of 0, and a y coordinate of whatever the screen height is, minus the size of the button (44 points) This puts it at the bottom of the screen.
We set the title to simply “+” to indicate adding a new item, set the background color, and add a target.

The target is a selector that fires when the button is clicked. In our case, the selector is called “addNewItem”, which means we need to create this as a function…

let addItemAlertViewTag = 0
let addItemTextAlertViewTag = 1
func addNewItem() {
    
    var titlePrompt = UIAlertController(title: "Enter Title",
        message: "Enter Text",
        preferredStyle: .Alert)
    
    var titleTextField: UITextField?
    titlePrompt.addTextFieldWithConfigurationHandler {
        (textField) -> Void in
        titleTextField = textField
        textField.placeholder = "Title"
    }
    
    titlePrompt.addAction(UIAlertAction(title: "Ok",
        style: .Default,
        handler: { (action) -> Void in
        if let textField = titleTextField {
            println(textField.text)
        }
    }))
    
    self.presentViewController(titlePrompt,
        animated: true,
        completion: nil)
}

This function will create a UIAlertController object with a text field by using the addTextFieldWithConfigurationHandler method. I’ll probably go over this as a separate blog post at some point, but for now it’s enough to know that when the handler on the “Ok” action gets called, the textField’s text value is inside of textField.text. We can use this to take input from the user without adding too much more view work.

Okay, so now that we can detect the text entered, we can call a function to save the new item. We’ll call it saveNewItem:title

func saveNewItem(title : String) {
    // Create the new  log item
    var newLogItem = LogItem.createInManagedObjectContext(self.managedObjectContext!, title: title, text: "")
    
    // Update the array containing the table view row data
    self.fetchLog()
    
    // Animate in the new row
    // Use Swift's find() function to figure out the index of the newLogItem
    // after it's been added and sorted in our logItems array
    if let newItemIndex = find(logItems, newLogItem) {
        // Create an NSIndexPath from the newItemIndex
        let newLogItemIndexPath = NSIndexPath(forRow: newItemIndex, inSection: 0)
        // Animate in the insertion of this row
        logTableView.insertRowsAtIndexPaths([ newLogItemIndexPath ], withRowAnimation: .Automatic)
    }
}

Our method will take in the title as the only argument, and use the createInManagedObjectContext function we created earlier to add a new record with the title of title. For now, we can leave the text blank.

Similar to how we did before with the deletion, we need to call fetchLog() to update our table view’s backing array, logItems.

Finally, we can create our NSIndexPath for the new item, and called insertRowsAtIndexPaths to animate in the new row.

Now, we just need to call the new method from the handler closure in the titlePrompt’s “Ok” button:

titlePrompt.addAction(UIAlertAction(title: "Ok",
    style: .Default,
    handler: { (action) -> Void in
    if let textField = titleTextField {
        self.saveNewItem(textField.text)
    }
}))

Running the app you can now add and delete rows to our table, backed by Core Data. If you close the app and restart it, you may notice that the data is reset every time. This is happening because Core Data doesn’t persist automatically, you must explicitly call save for that behavior. So as a final step in this tutorial section, let’s create a save() method in our ViewController.swift file.

func save() {
    var error : NSError?
    if(managedObjectContext!.save(&error) ) {
        println(error?.localizedDescription)
    }
}

The API is pretty simple just call save() on the managedObjectContext. Optionally you can include an NSError pointer to capture errors. The save() function returns true on success, and false on failure. If the save succeeds, your data will now persist.

Let’s call our save() method after the user adds or removes an item.

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
...
    // Tell the table view to animate out that row
     logTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
     save()
...
func saveNewItem(title : String) {
...
    if let newItemIndex = find(logItems, newLogItem) {
        // Create an NSIndexPath from the newItemIndex
        let newLogItemIndexPath = NSIndexPath(forRow: newItemIndex, inSection: 0)
        // Animate in the insertion of this row
        logTableView.insertRowsAtIndexPaths([ newLogItemIndexPath ], withRowAnimation: .Automatic)
        save()
...
}

Running the app we now have a super nifty app where we can add or remove items from a list with neat animations!

This concludes part 3 of the Core Data tutorial for now. The full source code can be found here.

In the next section, we’ll set up migrations so you can make changes to the schema of your live apps. Core Data Migrations Tutorial in Swift ».

As always, be sure to subscribe to my newsletter for tutorial updates and more, and take a look at my upcoming book, which is now in early access.

Did this tutorial help you?

Support my Patreon

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

Subscribe via RSS

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.

Did this tutorial help you?

Support my Patreon

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

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.

Did this tutorial help you?

Support my Patreon

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

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()
    }

}

Did this tutorial help you?

Support my Patreon

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

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.

Did this tutorial help you?

Support my Patreon

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

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.

Did this tutorial help you?

Support my Patreon

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

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.

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

Did this tutorial help you?

Support my Patreon

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

Subscribe via RSS