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

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. In Part 1, we created a touch-based way to manually control focus. If you haven’t read that yet you can read it here.

In this part of the tutorial, we’re going to dive in to the API a little deeper and create a second axis for controlling our image using the vertical touch position.

First off, let’s just clean up this code that determines how far in to the screen is being touched. Let’s create a function that takes in a UITouch object, and returns a CGPoint specifying how far in to the screen we’ve tapped, as a percentage of the screens total width. For example tapping on the very top-right of the screen will return a CGPoint with values 1,1. While tapping on the bottom-left will return 0,0… the center of the screen 0.5, 0.5, and all values in between.

func touchPercent(touch : UITouch) -> CGPoint {
    // Get the dimensions of the screen in points
    let screenSize = UIScreen.mainScreen().bounds.size
    
    // Create an empty CGPoint object set to 0, 0
    var touchPer = CGPointZero
    
    // Set the x and y values to be the value of the tapped position, divided by the width/height of the screen
    touchPer.x = touch.locationInView(self.view).x / screenSize.width
    touchPer.y = touch.locationInView(self.view).y / screenSize.height
    
    // Return the populated CGPoint
    return touchPer
}

This declares a method named touchPercent, which takes a UITouch object named touch as an argument, and returns a CGPoint. The actual math is simple… take the touched point and divide by the total number of points for either the width or height of the screen. We are already using the x value for focus, but we need to adjust the touchesBegan and touchesMoved method to use our new function.

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    let touchPer = touchPercent( touches.anyObject() as UITouch )
    focusTo(Float(touchPer.x))
}

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
    let touchPer = touchPercent( touches.anyObject() as UITouch )
    focusTo(Float(touchPer.x))
}

Note: you can remove the old screenWidth variable we were declaring before.

Next, we want to specify the ISO. If you’re unfamiliar with that is, it’s simple enough to say that the ISO is a setting for digital cameras that specifies what amount of light to allow in to the lens. A lower ISO will give brighter and higher quality pictures, but with a slower shutter speed, resulting in blurrier photos. A very low ISO results in a camera mode where a tripod is useful. With a very high ISO, you can take quick action shots with the very low shutter speed, but expect to see lower quality images with more noise. I’m by no means a professional photographer, so I’m probably not explaining it all that well, but that’s about my level of understanding.

Okay, moving on… let’s just add it to the app and see what it does for ourselves 🙂

First, let’s update the focusTo() method to take in an isoValue in addition to a focusValue

func updateDeviceSettings(focusValue : Float, isoValue : Float) {
    if let device = captureDevice {
        if(device.lockForConfiguration(nil)) {
            device.setFocusModeLockedWithLensPosition(focusValue, completionHandler: { (time) -> Void in
                //
            })
            
            // Adjust the iso to clamp between minIso and maxIso based on the active format
            let minISO = device.activeFormat.minISO
            let maxISO = device.activeFormat.maxISO
            let clampedISO = isoValue * (maxISO - minISO) + minISO
            
            device.setExposureModeCustomWithDuration(AVCaptureExposureDurationCurrent, ISO: clampedISO, completionHandler: { (time) -> Void in
                //
            })
            
            device.unlockForConfiguration()
        }
    }
}

The first new thing in this function is the name is now updateDeviceSettings. This is just because we’re adding more than focus. We’ve also specified focusValue and isoValue as distinct values.

Next, we have the normal locking and setting of the focus, followed by an additional bit of code that stored the minISO and maxISO, then adjusts our isoValue to fit within those two values, proportionally.

For example, if minISO is 50, and maxISO is 100. A value of 0 passed on to isoValue will result in a clampedISO of 50. But a value of 0.5 will result in a clampedISO of 75.

After adjusting this value, we can call the method that actually sets the ISO, setExposureModeCustomWithDuration().

The first parameter to this method is the shutter speed, in this case we aren’t trying to specify shutter speed so we simply use the constant AVCaptureExposureDurationCurrent. This basically just says we don’t want to specify a shutter time, and are just modifying the ISO. Second, is our clampedISO value, then finally we have a completionHandler just as we do with the setFocusModeLockedWithLensPosition method.

Try running the app and sliding your finger from the top to the bottom of the screen. Do you see the difference in the general pixel brightness? This is the ISO being modified in real time.

In the next part, we’ll dig a little deeper and naturally, implement some more UI to control these settings.

Here is the final code from this post:
Part 2 on Github


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

Swift’s Nil Coalescing Operator In Xcode 6 Beta 5

I was just updating my tutorial series on developing iOS 8 apps in Swift for Beta 5, and I was looking at this bit of code:

var name = result["trackName"] as? String
if !name? {
    name = result["collectionName"] as? String
}

The code is setting name to the result[“trackName”], if it can successfully cast to String. If it can’t then maybe it’s blank, or null, or something’s just not right.

So, then on line 2, it checks to see if name is not set still by using !name?, checking to see if it’s nil. If so, then switch over to the “collectionName” key. Sometime’s this example API call returned names of songs as “trackName”, and sometimes it returned names of albums in the key “collectionName”

All this code is really just checking/setting the variable name in order to get something in there. But it’s sort of verbose, and the Xcode 6 Beta 5 update actually made optionals no longer conform to BooleanType any more, so this code gives an error on this line:

if !name?

ERROR: Type ‘String?’ does not conform to protocol ‘BooleanType.Protocol’



Apple’s Beta 5 update notes mentions this change, and the new preferred method of performing this check. From the release notes:

“Optionals no longer conform to the BooleanType (formerly LogicValue) protocol, so they
may no longer be used in place of boolean expressions (they must be explicitly compared with v != nil). ”

So I could update my code to say something like this:

var name = result["trackName"] as? String
if name == nil {
    name = result["collectionName"] as? String
}

This works, but beta 5 added one more thing, the nil coalescing operator! That’s quite a mouthful, and if you use that term at parties people will think you’re super smart. They probably will not want to talk to you much after that, but it’s just because they are jealous.

Okay, so back on topic. The nil coalescing operator…

Basically this new operator does the same check as above, but it does it in short form using an operator, ??

Placing it between two operands does the check with the left hand value. If it’s nil it then prefers the right-hand value. So for example, I can rewrite my statement above as the following:

let trackName = result["trackName"] as? String
let collectionName = result["collectionName"] as? String
let name : String = trackName ?? collectionName

This code is a bit more concise, with pretty much the same output. I can also easily add a third option to fall back on if for some crazy reason neither of these variables are set:

let name = trackName ?? collectionName ?? "Untitled"

By doing this we get an added benefit: the type “String” can now be inferred 🙂
The only downside is that both the trackName and collectionName lookups are performed. We could eliminate this as well by rewriting this as a single (slightly harder to read) line.

let name = (result["trackName"] as? String) ?? (result["collectionName"] as? String) ?? "Untitled"

But personally, I think it’s cleaner to leave it as three lines, even if it is slightly less performant.

Looking at the definitions for the ?? operator I see this:

infix operator ?? {
    associativity right
    precedence 110
}
.....
func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T

You’ll notice the left-hand operand is of type T?, and the right-hand operand is of type @autoclosure () -> T. Or in other words, the left-hand side takes an optional, and the right-hand side takes basically any expression (any closure, except you don’t need to specify it’s a closure, the @autoclosure keyword turns it in to one.) That means the right-hand operand can take just about any expression, such as:

isEnabled = screenSaverDisabled ?? self.lastTimeSeenMoving > (CACurrentMediaTime()-5)

// -or-

var someValue = someOptional ?? self.someMethod() ?? self.someProperty

The only requirement is that each of these operands are of the same type.

Happy coalescing!

Excited to learn more? Take a look at my upcoming book.


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

Subscribe via RSS

Access Control In Swift

Updated December 21 for Xcode 6.1.1

The Swift feature of Access Control is really important from a software architecture perspective, because it allows us to properly implement encapsulation. Without the ability to hide members and methods of classes, it’s very easy to accidentally (or not) reach in to classes and mess with internals that were not designed to be directly modified.

Swift offers three levels of access control: public, private, and internal.

public makes the entities visible anywhere within the module, or from other modules (as long as the module is imported)
internal makes the entities visible only within the same module. This is the default behavior in Swift.
private makes the entities visible only within the same source file.

To demonstrate Swift’s new access controls, let’s build a small math class for Swift. For now, it’ll be really simple. It’s a class that has two properties of type Double, and has a single computed property named sum. If you want to follow along, just create a single-view template project and add this in to an otherwise empty swift file called Math.swift

import Foundation

class QMath {
    
    var num1: Double?
    var num2: Double?
    
    var sum: Double {
        return num1! + num2!
    }
    
    init(_ num1: Double, _ num2: Double) {
        self.num1 = num1
        self.num2 = num2
    }
    
}

The class QMath has a constructor that takes two parameters, num1 and num2 of type Double. If you haven’t seen the underscores before, those are the external parameter names, which we’ve set to not be specified by using the underscore. If we instantiate a QMath instance in another class we can get the sum property and confirm it works as expected.

If you’re following along, you insert this in to your applicationdidFinishLaunchingWithOptions: method inside of AppDelegate.swift

var m = QMath(4, 50)
println(m.sum)
54.0

You’ll notice that in our sum getter, we implicitly unwrap num1 and num2 using the exclamation mark (!). Although they’re both optionals, we can make this assumption if we know the init(,) method we’ve provided is called, because the arguments to that method are not optional and will definitely be set upon initialization.

Except one minor problem:
We could actually set either of those values to be nil.

var m = QMath(4, 50)
m.num1 = nil
println(m.sum)

This code compiles just fine, num1 is Optional and can be set to nil, not an issue. However, calling the sum getter now implicitly unwraps a nil optional. If you run this code you’ll see the following error:

fatal error: unexpectedly found nil while unwrapping an Optional value

We could simply set the num1 and num2 properties to not be optional, which would require we have a default value such as 0. But, an better approach that allows us to avoid the unnecessary initial value is to simply disallow modifications to these internal variables. We want this class to be a black box, where num1 and num2 can’t directly be modified.

So, to solve our above mentioned issue, we can make the two number properties private.

private var num1: Double?
private var num2: Double?

Attempting to build the app again now produces an error on the line we used to set num1 to nil.

m.num1 = nil
'QMath' does not have a member named 'num1'

Our external reference to the num1 property is no longer valid, it has no visibility to num1 and therefore this line of code is an error. There’s only one thing to do now, remove this line and start using the class as it was designed to be used! Mission accomplished!

If you want to dive deeper and tinker with Swift, it’s a good idea to read my post on Running Swift Scripts From The Command Line. When you’re ready to get serious make sure to also learn about my upcoming Swift eBook and video tutorial series.

Warning: Incoming opinion
Good software design principles suggest everything should be private by default, and entities should be exposed deliberately on an as-needed basis. This makes it easier to write more modular code and leads to cleaner programming interfaces. Disagree? Yell at me about it on Twitter.


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

Subscribe via RSS

Developing iOS 8 Apps Using Swift – Animations, Audio, and Custom Table View Cells (Part 7)

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

In parts 1 through 6 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

In this tutorial we’re going to implement an Album detail view, that makes a second API call to retrieve a list of tracks for an album, downloads higher resolution album art, and allows of to play previews of the tracks within our app. As an optional extra, we are going to also implement some nifty animations using the Core Animation API provided by the iOS SDK. When we’re done, we’re going to have something like this (video taken in iOS 7 Simulator)

Setting up our API Controller

Because we’re going to be adding additional API calls in this part of the tutorial, we should modify our API Controller for some code re-use. Let’s start with a more generic get request.

In your API Controller add the function get(), which takes path as a String argument, and converts it to an NSURL:

func get(path: String) {
    let url = NSURL(string: path)
    ...

Now get the NSURLSession and send it using dataTaskWithURL as we did before, in fact the code is exactly the same as what is currently inside of our searchItunesFor() function, so just copy and paste it from there. Start cutting right after the line

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

And move everything in to the get() method. Your complete APIController.swift file should look something like this now:

import Foundation

protocol APIControllerProtocol {
    func didReceiveAPIResults(results: NSArray)
}

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

Now in our searchItunesFor function, we can simply call on our new get() function and slim it down to the bare essentials. Just add a call to the get(urlPath) method on the end. The final method should look like this:

func searchItunesFor(searchTerm: String) {
    // The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs
    let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
    
    // Now escape anything else that isn't URL-friendly
    if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
        let urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music&entity=album"
        get(urlPath)
    }
}

See the difference? The only part that was specific to the search function was the escaping of search terms, and embedding the term inside of the URL, so there’s no reason not to just break the get() part out in to it’s own method.

Now, we can quickly add a second API function to lookup a specific album. But first, let’s modify our album model to store a collectionId variable, used by iTunes to identify individual albums.

In our Album struct, add a new variable collectionId of type Int.

let collectionId: Int

..modify the constructor to accept collectionId as an argument, and add a line to set the collectionId as one of our variables being passed in through init()

init(name: String, price: String, thumbnailImageURL: String, largeImageURL: String, itemURL: String, artistURL: String, collectionId: Int) {
    self.title = name
    self.price = price
    self.thumbnailImageURL = thumbnailImageURL
    self.largeImageURL = largeImageURL
    self.itemURL = itemURL
    self.artistURL = artistURL
    self.collectionId = collectionId
}

Great! We can now initialize Albums with a collectionId, but now our existing albumsWithJSON code is wrong, it’s missing the collectionId parameter.
Find the line that creates the newAlbum just before it appends to the array returned from albumsWithJSON().

Modify this to get the collectionId our of the result dictionary, and pass it in to the Album constructor. Since we really need the collectionId to not be nil in order for this app to work, we’ll bundle the whole album creation inside of an if let clause so that only valid albums will show up on the list.

if let collectionId = result["collectionId"] as? Int {
    var newAlbum = Album(name: name!,
        price: price!,
        thumbnailImageURL: thumbnailURL,
        largeImageURL: imageURL,
        itemURL: itemURL!,
        artistURL: artistURL,
        collectionId: collectionId)
    albums.append(newAlbum)
}

The reason we need to add this collectionId variable is so that we can perform lookups of albums when they are selected. With the collectionId, it’s easy to do a second query of the iTunes API to gather lots of details about an individual album. For example, we can get a list of tracks with media URLs that will give us a 30 second preview.


Setting up the Details View

In the last tutorial we added a DetailsViewController to our storyboard. Let’s add a TableView to this view as well. You can lay it out however you like, but I recommend giving the Table View the majority of the screen space. This is where we’re going to load in our list of tracks.

Let’s now connect this new TableView to a property in DetailsViewController called tracksTableView.

@IBOutlet weak var tracksTableView: UITableView!

Now, set the dataSource and delegate of the table view to the DetailsViewController, and implement the protocol as we did before:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 0
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return UITableViewCell()
}

It’s probably useful at this point to try and run the app. You should be able to drill in to an album and see an empty list of tracks.


Everything working? Cool, let’s keep going…

If we’re going to show tracks we’re going to need another model. Create a new Swift file called ‘Track.swift’, and give it three String properties for title, price, and previewUrl.

import Foundation
struct Track {
    let title: String
    let price: String
    let previewUrl: String
    
    init(title: String, price: String, previewUrl: String) {
        self.title = title
        self.price = price
        self.previewUrl = previewUrl
    }
}

This model is set up in pretty much exactly the same way as the Album model, not much new here.

In DetailsViewController, let’s add an array of tracks as a new property.

var tracks = [Track]()

Now, to get track information for the album, we need to modify our API Controller again. Fortunately for us, we have an easy to use get() function that makes this pretty simple.

Let’s add a new function to APIController that takes an Int collectionId argument, and tell it to use get() to get track information

func lookupAlbum(collectionId: Int) {
    get("https://itunes.apple.com/lookup?id=\(collectionId)&entity=song")
}

We’re going to need to use this in our DetailsViewController, so we now need to implement the APIControllerProtocol we wrote earlier in to DetailsViewController. So modify the class definition of DetailsViewController to include this, and our api object.

class DetailsViewController: UIViewController, APIControllerProtocol {
    lazy var api : APIController = APIController(delegate: self)
    ...

Your project will have an error at this point about the protocol we haven’t yet implemented, but that’s ok let’s keep moving.

In the DetailsViewController viewDidLoad method, we want to add a portion to pull down tracks based on the selected album, so let’s add the following:

// Load in tracks
if self.album != nil {
    api.lookupAlbum(self.album!.collectionId)
}

This is all stuff we’ve seen before. We create an instance of our APIController with the delegate set to self, and use our new lookupTrack method to get details on the tracks in the selected album. Here we use the lazy keyword to indicate we don’t want the APIController instance api to be instantiated until it is used. We need to do this to avoid the circular dependency of DetailsViewController needing to be initialized to pass it in as an argument to the APIController(delegate:) constructor. Earlier we used an optional APIController to solve this problem, but using the lazy keyword is another way to solve this problem and it’s a little cleaner.

To fully adhere to our APIControllerProtocol, we need to implement the didReceiveAPIResults() function in this class too. We’ll use this to load in our track data. We’ll implement this exactly as we did for the SearchResultsViewController, by offloading the responsibility of converting the JSON response in to a list of tracks to the Track model.

// MARK: APIControllerProtocol
func didReceiveAPIResults(results: NSArray) {
    dispatch_async(dispatch_get_main_queue(), {
        self.tracks = Track.tracksWithJSON(results)
        self.tracksTableView.reloadData()
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    })
}

We’re using a non-existent tracksWithJSON() static method on Track. So we need to add that before this will compile. Open up Track.swift and add a method similar to our albumsWithJSON method.

static func tracksWithJSON(results: NSArray) -> [Track] {
    var tracks = [Track]()
    for trackInfo in results {
        // Create the track
        if let kind = trackInfo["kind"] as? String {
            if kind=="song" {
                var trackPrice = trackInfo["trackPrice"] as? String
                var trackTitle = trackInfo["trackName"] as? String
                var trackPreviewUrl = trackInfo["previewUrl"] as? String
                if(trackTitle == nil) {
                    trackTitle = "Unknown"
                }
                else if(trackPrice == nil) {
                    println("No trackPrice in \(trackInfo)")
                    trackPrice = "?"
                }
                else if(trackPreviewUrl == nil) {
                    trackPreviewUrl = ""
                }
                var track = Track(title: trackTitle!, price: trackPrice!, previewUrl: trackPreviewUrl!)
                tracks.append(track)
            }
        }
    }
    return tracks
}

This API call returns the album before it returns the list of tracks, so we also add a check to make sure the “kind” key is set to “song”, as you see on line 8. Otherwise this function is just extracting some data from the JSON; Then we check that the three fields we need aren’t null, and if so set some reasonable defaults.

Now in DetailsViewController let’s modify the numberOfRowsInSection to be the track count

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

And let’s modify the cellForRowAtIndexPath method to load in our track data.

First, we need to add a prototype cell to the TableView in our storyboard, because we’re going to use a custom cell.
So select the Table View in the storyboard, and set the number of prototype cells to 1.
Then, select the cell itself and set the Identifier to “TrackCell” in the Attributes Inspector (on right-hand panel while selecting the Table View.)

Adding a Custom Table View Cell

To demonstrate what the prototype cells are really for, I think we should add some custom controls to this one. Create a new Swift class called TrackCell that inherits from UITableViewCell, and give it two IBOutlet UILabels called playIcon and titleLabel.

Now, back in your Storyboard file. Change the prototype cell’s class to ‘TrackCell’ under the Identity Inspector in the right-hand panel.
Next, add two UILabel’s to the cell by dragging the views on to the cell itself. Put one on the left for our play/pause button, and one taking up most of the space on the right to say the title of the track.

Drag two labels on to the prototype cell. Make one of them small and on the left, around 23×23 points, for a ‘Play/Stop’ icon. The second one will be the track title and should take up the rest of the cell. Click in to your play button label and then in the Mac OS menu bar hit Edit->Emoji & Symbols and find a play button looking icon. I found some under Emoji->Objects & Symbols. As an optional challenge, try using an image for the button icon!

import UIKit
class TrackCell: UITableViewCell {
    @IBOutlet weak var playIcon: UILabel!
    @IBOutlet weak var titleLabel: UILabel!
}

When you’re done you should have a prototype cell looking something like this:

In the DetailsViewController, we can now implement the custom cells by getting the TrackCell object and casting it to our class with ‘as TrackCell’

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("TrackCell") as! TrackCell
    let track = tracks[indexPath.row]
    cell.titleLabel.text = track.title
    cell.playIcon.text = "YOUR_PLAY_ICON"
    return cell
}

The logic is mostly the same as our other table view, with the exception that we cast cell to our custom class, TrackCell, on the first line. The “YOUR_PLAY_ICON” text should be replaced with the play icon, which again, you can get by hitting Edit->Emoji & Symbols in the Mac OS menu bar. Don’t forget to put quotes around it!

Next we grab the track we need from our tracks array, just as before with albums.

Finally we access our custom IBOutlet variable, titleLabel, set it’s text to be the track title, and do the same with playIcon.


Congrats on getting this far, we’re in the home stretch!


Play some music

Okay, next we want to set up a way to actually hear some audio. We’re going to use the MPMoviePlayerController class to do this. It’s easy to work with, and works just fine with audio-only streams.

First off, in our DetailsViewController class let’s add the mediaPlayer as a property, right under the class definition add:

var mediaPlayer: MPMoviePlayerController = MPMoviePlayerController()

ERROR! Use of undeclared type MPMoviePlayerController.

It’s okay, this is just because we need to import the framework MediaPlayer, it isn’t included by default in our project.

Just add the following to the top of your DetailsViewController:

import MediaPlayer

Next, let’s kick off the audio playing when a user selects one of the track’s rows. Add the following to our DetailsViewController:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    var track = tracks[indexPath.row]
    mediaPlayer.stop()
    mediaPlayer.contentURL = NSURL(string: track.previewUrl)
    mediaPlayer.play()
    if let cell = tableView.cellForRowAtIndexPath(indexPath) as? TrackCell {
        cell.playIcon.text = "YOUR_STOP_ICON"
    }
}

The line mediaPlayer.stop() stop’s the currently playing track. If there isn’t one playing, nothing happens. We don’t want to play multiple tracks at once so let’s make sure we stop a track if another one is clicked 🙂

Next, mediaPlayer.contentURL sets a url for where the media player should load it’s content. In our case it’s from the url stored in track.previewUrl.

Finally, we call mediaPlayer.play(), and get the track cell for the tapped row index.
If this row is still visible, it’ll set ‘cell’ and here we can change the playIcon label to instead show the stopped icon, which we set again by using Edit->Emoji & Symbols on the Mac OS menu bar.

If you run your app, you should now have a fully working iTunes music preview application! This by itself is pretty great, but let’s add one more thing to make it even more slick, some smooth table view cell animations.

Adding Animations

This is actually really easy, and has a very cool visual effect.

All we’re going to do is add the following function to both our SearchResultsViewController, and our DetailsViewController:

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    cell.layer.transform = CATransform3DMakeScale(0.1,0.1,1)
    UIView.animateWithDuration(0.25, animations: {
        cell.layer.transform = CATransform3DMakeScale(1,1,1)
        })
}

Now run the app and scroll around, neat right?

So how’s it work?
The function willDisplayCell is called from the TableView delegate, similar to the rest of our callback functions that set up the row. But this one is only called the moment before a cell appears on-screen, either through initial loading or through scrolling.

cell.layer.transform = CATransform3DMakeScale(0.1,0.1,1)

This first line uses CATransform3DMakeScale() to create a transform matrix that scales down any object in x, y, and z. If you are familiar with linear algebra you’ll know what this means right away. If not, it’s not super important. The point is, it makes things scale, and here we’re scaling it down to 10% by setting the x and y values to 0.1.

So, we are basically just setting the cell layer’s transform to be 90% smaller.

Next we set the cell layer’s transform to a new scale, this time of (1,1,1). This just means that it should return to it’s original scale. Because this line is run inside of the animateWithDuration() block, we get the animation for free courtesy of Core Animation.

Experienced Obj-C developers will probably recognize this is not the only way to perform such an animation. However, I believe this method is the easiest to understand, in addition to being the most Swifty.

In my upcoming book I go in to great detail about how to efficiently use Core Animation to make beautiful animations for your apps. Using Core Animation in this way really makes your app pop.

The full source code for this section is available here.

A reader of this tutorial series contributed the next section, which covers producing a nicer play/pause icon purely in code. Check it out here.
Make sure to sign up to be notified of the next tutorial series.


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 3 – Best Practices

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

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

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

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

Split up the code

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

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

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

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

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

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

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

import Foundation

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

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

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

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

let api = APIController()

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

api.searchItunesFor("Angry Birds")

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

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

Defining an API protocol

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

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

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

 

protocol APIControllerProtocol {
   func didReceiveAPIResults(results: NSArray)
}

 

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

Adhering to the protocol

Now modify your SearchResultsViewController to adhere to the protocol:

class SearchResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {

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

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

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

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

Using the Protocol

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

var delegate: APIControllerProtocol?

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

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

api.delegate = self

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

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

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

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

import Foundation

protocol APIControllerProtocol {
    func didReceiveAPIResults(results: NSArray)
}

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

Common issue here

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

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

PHEW!

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

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

Full code for this section visible here.

Go to Part 4 ->

 


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

Subscribe via RSS

Swift Tutorial – Developing iOS Apps Part 1

Swift Tutorial

iOS Development moves to Swift

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

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

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

Tutorial Index

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

The Basics

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

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

var myString = "This is my string."

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

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

let someConstant = 40

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

let someOtherConstant: Int = 40

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

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

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

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

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

Hello World

Hello World in Swift and iOS

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

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

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

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


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

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

Replace this line with our amazing hello world code:

print("Hello World")

Hello World Swift Code

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

Hello World Output

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

Adding a Table View

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

UITableView drag on to StoryBoard

The empty table view in the iPhone Simulator:

The empty table view in the iPhone Simulator

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

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

Setting up the UITableView Data Source and Delegates

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


Connecting Storyboard Objects to Code in Xcode

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

So open ViewController.swift and modify this line:

class ViewController: UIViewController {

to this:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

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

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

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

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

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

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

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

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

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

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

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

You’re doing great!

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

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

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

Full code for this part here »

Go To Part 2 »

Get The Source
Next Part


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

Subscribe via RSS

Using open source iPhone app components with Cocoa Pods

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

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

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

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

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

platform :ios, ‘6.0’

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

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

$ pod install

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

$ gem install cocoapods

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

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

So to recap:

1. Install ruby

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

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

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

3. Install cocoapods

$ gem install cocoapods

4. Create a Podfile in your Xcode project directory

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

6. Run pod install

$ pod install

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

8. Enjoy!

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


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

Subscribe via RSS