Swift 3 Tutorial – Fundamentals

Swift 3 Tutorial

Swift 3 Tutorial

In this Swift 3 tutorial, we’ll focus on how beginners may approach going from complete beginner to having a basic grasp on Swift, and we’ll be working with Swift 3. We chose to write this tutorial because newcomers will find many tutorials out there that are out of date, so it’s not appropriate to simply write a “what’s new in Swift 3” post. Instead we’ll approach this from the perspective of someone who has some programming experience in at least one other language, and we’ll teach you what’s unique about Swift, and how to use it effectively. Ready? Let’s go!


Constants and Variables

Any Swift variable is either a constant or not. Not to be confused with the type of the variable, such as Float or Int, constants and variables are just ways to describe terms that hold a value which can change (are mutable), and constants that can not be changed (becuase they are immutable).

To define a constant, use the let keyword.

Example:

let name = "Jameson"

If you were to try and change the value of name, you would be unable to do so, and the Swift compiler would produce an error.

let name = "Jameson"
name = "Bob"
error: cannot assign to value: 'name' is a 'let' constant
name = "Bob"
~~~~ ^

On the other hand, by using the var keyword, we define a variable that can change:

var name = "Jameson"
name = "Bob"

This code does not produce an error.

In general, you should always default to using the let keyword, unless you know you need a var keyword. This leads to code that is ultimately safer. If you define a constant, and later attempt to modify it, you will get an error and at that time can determine if you should switch to use the var keyword, or if the error is giving you a hint that maybe you should rethink the current logic flow. In general, immutability is preferred over mutability; it simply leads to less programmers errors and makes it easier to reason about your code.


Basic Types

In Swift, a type is indicated by declaring a variable, then putting a colon, folowed by the type name. For example to declare an integer, which is of Swift type Int, you could use the following:

let age: Int = 5

Or similarly, if you want to declare a string:

let name: String = "Jameson"

Swift supports type inference, so you can usually omit the type information, and let the compiler determine what the type should be based on it’s initial value.

let age = 5
let name = "Jameson"

The types for age and name are still Int and String respectively, but we can skip the type annotation, because it is obvious that 5 is an Int, and "Jameson" is a String.

Remember, the let keyword simply makes these values constant. If we expected the value of age to change, but not name, we might write these declarations like so:

var age = 5
let name = "Jameson"

Now if we want to update the age value, it’s possible to do:

var age = 5
let name = "Jameson"
age = 25
print(age)
25

Working with Strings

It’s frequently useful to print a command to the console or otherwise evaluate a String containing other variables. For example, I might want to form a sentence with my variables age and name and print it to the console. I can do this using the + operator between each String.

let age = "15"
let name = "Robb"
 
let sentence = name + " is " + age
print(sentence)
Robb is 15

A shortcut for this is to write your String as you normally would without the + operator seperating each string, and put each variable inside of a set of parentheses, preceeded by a backslash \.

let sentence = "\(name) is \(age)"
print(sentence)
Robb is 15

As you can see this has the same effect, but is much easier to read and compose sentences.

One thing you may have noticed is that age is now of type String because it was assigned the value "15" instead of just 15 without the quotes. This is because concatenating a String and an Int will not automatically cast the Int to String, which is a neccessary step before concatenating is possible.

Or in other words, this code will produce an error:

let age = 15
let name = "Robb"
 
let sentence = name + " is " + age
print(sentence)
Error: Binary operator '+' cannot be applied to operands of type 'String' and 'Int'

So what we have to do is get age converted to a String before using it here. Thing is done by casting the variable. Simply call the String constructor and pass in the Int value:

let age = 15
let name = "Robb"
 
let stringAge = String(age)
 
let sentence = name + " is " + stringAge
print(sentence)
Robb is 15

We created a new variable called stringAge here, but we also could have performed the cast in-place, because the string interpolation will evaluate each expression separately, and the same goes for the contents of the parenthesis when interpolating strings that way:

let age = 15
let name = "Robb"
 
let sentence = name + " is " + String(age)
print(sentence)
print("\(name) enjoys being \(String(age))")
Robb is 15
Robb enjoys being
15

Optionals

In Swift, there is also the concept of the optional. An optional is just a variable that can be nil, null, or otherwise not set to any value. In general, you can think of any variable in most other programming languages as being an optional. The “optionality” of a variable is declared by appending a question mark (?) on to the end of the type name in a type annotation. So continuing the example above, where we know age and name will always be set, we might want to add another variable that could be nil, because it is possible that it just isn’t present. Let’s use favoriteColor as an example. Many people have a favorite color, but it’s possible someone doesn’t, or we just don’t have the data. We would declare this variable as an optional, and not assign it to any value.

var favoriteColor: String?

Implicit in the declaration of an optional with no value set, is the assignment to nil. We can verify this by examining the value of favoriteColor after declaring it as an optional by printing it to the console using the print() function.

var favoriteColor: String?
print(favoriteColor)
nil

We can later assign something to favoriteColor and see that it is no longer nil.

var favoriteColor: String?
favoriteColor = "Blue"
print(favoriteColor)
Optional("Blue")

Note that instead of just getting the string "Blue", we get Optional("Blue"). This is because the value is still “wrapped” inside of the optional.

You can think of optionals like a birthday gift. The box the gift comes in, wrapped up in some paper with pretty balloons on it, could actually be empty. A rather cruel gift to give someone on their birthday, but it’s possible to do. It could also have something inside of it. But either way, if we pick it up and look at it, what we have is not the thing inside of of it in our hands, but it’s just the wrapped box itself.

If we want to get at the thing inside, we need to unwrap the gift first. This is the same way optionals work. When we pass around optional variables and interact with them, we’re really working with a container that may or may not have anything inside of it. Similar to our gift, the optional must be unwrapped before it can be used.

Declaring our optional with no value is valid Swift and will compile just fine. But, if we tried to declare this variable without the optional syntax, it would present an error.

There are also variables in Swift that are not optional. They always have a value. If you tried to assign nil to a non-optional variable, you will get a compiler error:

var favoriteColor = "Blue"
favoriteColor = nil
error: nil cannot be assigned to type 'String'

Similarly, non-optional values can not be assigned to nil during their declaration:

var favoriteColor: String
error: variables must have an initial value

Unwrapping Optionals

So we know what optionals are, and that they allow for a variable to become nil, and we know that optionals are containers rather than values themeselves. So, in our programs when we want to access the contents of an optional, how do we do it? There are several ways so let’s go over them now.

First, and most commonly, you will unwrap optionals using optional binding. In optional binding, you will assign a new variable to the value of an optional within an if statement. If the optional contains a value, this variable will be set, and the code block following this statement will be executed.

Let’s look at an example. Here we will declare two optionals, one called favoriteAnimal which is set to Fox, and one set to favoriteSong, which we will not set (it will remain nil)

var favoriteAnimal: String?
var favoriteSong: String?
 
favoriteAnimal = "Fox"

Let’s employ optional binding to discover if each variable is set, and if so we’ll print a sentence containing the value to the console. First we’ll do it with favoriteAnimal.

if let unwrappedFavoriteAnimal = favoriteAnimal {
    print("Favorite animal is: " + unwrappedFavoriteAnimal)
}
Favorite animal is: Fox

In the event that the value is not set, we simply will trigger whatever is in the else block, or nothing at all if an else block isn’t specified.

if let unwrappedFavoriteSong = favoriteSong {
    print("Favorite song is: " + unwrappedFavoriteSong)
}
else {
    print("I don't know what your favorite song is!")
}
I don't know what your favorite song is!

If we need to unwrap multiple optionals, and we require all of them to proceed with a bit of logic, we need to check each one:

var favoriteAnimal: String?
var favoriteSong: String?
 
favoriteAnimal = "Fox"
favoriteSong = "Shake it Off"
 
if let unwrappedFavoriteSong = favoriteSong {
    if let unwrappedFavoriteAnimal = favoriteAnimal {
        print(unwrappedFavoriteSong + " " + unwrappedFavoriteAnimal)
    }
}

This gets kind of messy kind of fast, so Swift offers a shortcut to unwrap multiple variables at once:

var favoriteAnimal: String?
var favoriteSong: String?
 
favoriteAnimal = "Fox"
favoriteSong = "Shake it Off"
 
if let unwrappedFavoriteSong = favoriteSong,
    let unwrappedFavoriteAnimal = favoriteAnimal {
    print(unwrappedFavoriteSong + " " + unwrappedFavoriteAnimal)
}

Collections

Swift has multiple collection types, the most common of which being arrays, sets, and dictionaries.

Array

Let’s take a look at an example of an Array

let starks: [String] = ["Eddard", "Catelyn", "Robb", "Sansa"]

Here we have a basic Array, which is of type [String]. The square brackets indicate that this is an array of String objects, rather than just being a single String. As usual, Swift can infer this type data too, just by examining the initial assignment:

let starks = ["Robb", "Sansa", "Arya", "Jon"]

We can access elements of this array in a variety of ways, such as using an Int index, calling the various collections methods.

let starks = ["Robb", "Sansa", "Arya", "Jon"]
 
print( starks[0] )
print( starks[2] )
print( starks.first! )
Robb
Arya
Robb

You’ll note that arrays are zero-indexed, so the first element in the array "Robb" is accessed using starks[0]

Additionally, you may notice that while the first method returns an optional (and therefore is being force-unwrapped with the ! symbol), the indexed accessors does not return an optional. If you try to access an index in an array that is not present, your program will fail at runtime! So always check the length of arrays when accessing by index:

if starks.count >= 4 {
    print( starks[3] )
}

There are ways to make this type of checking automated, but it is not done by default for performance reasons.

Hashable Types / Dictionary

Dictionaries are able to store values based on a key, typically the key is of type String, but it could actually be many different Swift types. In this example we create a basic Dictionary with String keys and Int values for the age of each person:

let ages = ["Robb": 15, "Sansa": 12, "Arya": 10, "Jon": 15]

We can access these values by their String keys:

print( ages["Arya"]! )
print( ages["Jon"]! )
10
15

Note that we’re unwrapping these because they are optional values, and could potentailly be nil. It is generally safer to use optional binding to unwrap the value from a Dictionary, especially if you have reason to believe the value could often be nil.

if let aryasAge = ages["Arya"] {
    print("Arya is \(aryasAge) years old")
}
Arya is 10 years old

We can also store arrays inside of dictionaries, or dictionaries inside of arrays, or a mix of both.

let families = [
    "Stark": ["Robb": 15, "Sansa": 12, "Arya": 10, "Jon": 15],
    "Baratheon": ["Joffrey": 13, "Tommen": 8]
]
let tommensAge = families["Baratheon"]!["Tommen"]!
print("Tommen is \(tommensAge) years old")
Tommen is 8 years old

The type of houses here would be [String: [String: Int]]
Or in other words it is a dictionary with a String key, and it’s values are [String: Int], another dictionary with String keys and Int values.

Set

A Swift 3 Set is similar to an Array, except the values in a Set are unique and unordered.

Initializing a Set looks almost exactly like intitializing an Array, the only different is the type:

let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]

This code creates a new set of type String. The greater than / less than symbols < and > are used to indicate Swift generic types, including the types of a Set as shown here.

You’ll notice we included "Blue" twice in our list, but if we print out the contents of colors, we only see it once:

let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]
print(colors)
["Orange", "Red", "Blue"]

You may also notice that the ordering is inconsistent. Sets do not maintain any particular order for their contents.

We can not access members of a Set using indexes as we can with arrays, but instead we use the methods built-in to the Set type to add and remove objects. We can also call the contains method to check if the Set includes something.

var colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]
colors.insert("Black")
colors.insert("Black")
colors.remove("Red")
print(colors)
print(colors.contains("Black"))
print(colors.contains("Red"))
["Black", "Orange", "Blue"]
true
false

Constructing sets of objects is a common way to catalogue what is included or excluded in a list of things, as long as there is no need to order or have duplicates of the objects.

There are many methods I have not mentioned, and I would encourage you to read through Apple’s documentation on each of these three classes to further familiarize yourself with them.

Tuples

Tuples are not technicaly a collection, but instead simply multiple variables that can be passed around with a single identifier.

let fullName = ("Jameson", "Quave")

The type of the tuple here is (String, String), and we can manually access each numbered tuple element using dot-syntax, followed by the index:

let fullName = ("Jameson", "Quave")
print(fullName.1)
print(fullName.0)
Quave
Jameson

Tuples can also be deconstructed in to new variable names:

let (first, last) = ("Jameson", "Quave")
print(first)
Jameson

Since we’re not using the last name here, we could just ignore that value by using an underscore _ and still deconstruct the first name:

let (first, _) = ("Jameson", "Quave")
print(first)
Jameson

Tuples are useful when you have a method that you want to return multiple values.

Control Flow

Control flow in Swift looks pretty similar to other languages. At the most basic level we have the if and else statements.

if 10 > 5 {
  print("10 is greater than 5.")
}
else {
    print("10 is not greater than five.")
}
10 is greater than 5

You can alternatively put your conditions for an if statement in parenthesis:

if (10 > 5) {
...

Swift also support the switch statement, and checks at compile for whether or not you have exhaustively covered all possibilities. If you do not (or can not) specifically handle all possibiities, then you can use the default: case to handle everything not explicitly handled.

let name = "Jameson"
switch(name) {
case "Joe":
  print("Name is Joe")
case "Jameson":
  print("This is Jameson")
default:
  print("I don't know of this person!")
}
This is Jameson

Here because the value of name is "This is Jameson", we match the 2nd case and execute the line

...
  print("This is Jameson")
...

If however we set the name to be something not present in our list of cases, such as "Jason", the switch would fall back to the default case.

let name = "Jason"
switch(name) {
case "Joe":
  print("Name is Joe")
case "Jameson":
  print("This is Jameson")
default:
  print("I don't know of this person!")
}
I don't know of this person!

Loops and Collections

Swift 3 does not support the classical C-style for loops you may be used to, and instead opts for enumeration and for-each style loops for the for element in array syntax.

For example if we have an array names, and we want to print each one seperately, we can do so with a for loop:

let names = ["Robb", "Sansa", "Arya", "Jon"]
 
for name in names {
    print("Name: \(name)")
}
Name: Robb
Name: Sansa
Name: Arya
Name: Jon

This is great if you happen to want to loop over an array, but without C-style arrays, how would we loop over a series of numbers? The answer comes in the form of Swift’s Range and Stride. Let’s say we wanted to count to 10, by threes, we could do that by using a Range from 1 to 10 using the syntax 1...10. Then we could only print each number that is evenly divisible by three by using the modulo % operator and checking for a remainder of 0.

for i in 1...10 {
    if i % 3 == 0 {
        print(i)
    }
}
3
6
9

There is another option however, to only iterate every third item (or any arbitrary delta), known as a stride. A stride can be created using a variety of methods, but the most common is stride(from: , to:, by:) where the from value is where the stride starts, to is where it ends, and by is how much each value changes to approach the to. If that sounds a little confusing just look at this code sample

let byThrees = stride(from: 3, to: 10, by: 3)
for n in byThrees {
    print(n)
}
3
6
9

It’s almost readable in english, you might say you are “counting” from 3 to 10 by 3. Here we create a stride and store it in a variable named byThrees, but we could use it directly in the loop as well:

for n in stride(from: 3, to: 10, by: 3) {
    print(n)
}
3
6
9

Collections also all have an indices property that can be used to loops. This returns an array of the indices a collection has, useful for iterating or filtering some but not all of a collection. For example, back in our name collection we may want only the first three names, which we can retrieve like so:

let names = ["Robb", "Sansa", "Arya", "Jon"]
for nameIndex in names.indices {
    if(nameIndex < 3) {
        print(names[nameIndex])
    }
}
Robb, Sansa, Arya

There is also the enumerate method on collections, that allow you to get both the index and value from an array as you loop over it:

let names = ["Robb", "Sansa", "Arya", "Jon"]
for (index, name) in names.enumerated() {
    print("\(index): \(name)")
}
0: Robb
1: Sansa
2: Arya
3: Jon

There are still more ways to enumerate loop over objects in Swift 3, but these are the most commonly used.

You may notice in our for loop we are assigning to two variables at once, both index and name. These are seperated by commas and surrounded by parenthesis in orer to delineate there are two named variable we expect returned from the enumerated() method. These are techincally deconstructed tuples.

This concluded the fundamentals portion of the Swift 3 tutorial. Next, I’ll show you how to use what you’ve learned here in real-world scenarios. Keep up with these posts by subscribing below. You’ll be emailed when I make new posts, and post video tutorials and other neat stuff, never spam 🙂


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

Subscribe via RSS

Designing Animations with UIViewPropertyAnimator in iOS 10 and Swift 3

Designing Animations with UIViewPropertyAnimator in iOS 10 and Swift 3

This is part of a series of tutorials introducing new features in iOS 10, the Swift programming language, and the new XCode 8 beta, which were just announced at WWDC 16

UIKit in iOS 10 now has “new object-based, fully interactive and interruptible animation support that lets you retain control of your animations and link them with gesture-based interactions” through a family of new objects and protocols.

In short, the purpose is to give developers extensive, high-level control over timing functions (or easing), making it simple to scrub or reverse, to pause and restart animations, and change the timing and duration on the fly with smoothly interpolated results. These animation capabilities can also be applied to view controller transitions.

I hope to concisely introduce some of the basic usage and mention some sticking points that are not covered by the talk or documentation.

Building the Base App

We’re going to try some of the features of UIViewPropertyAnimator in a moment, but first, we need something to animate.

Create a single-view application and add the following to ViewController.swift.

import UIKit

class ViewController: UIViewController {
    // this records our circle's center for use as an offset while dragging
    var circleCenter: CGPoint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Add a draggable view
        let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
        circle.center = self.view.center
        circle.layer.cornerRadius = 50.0
        circle.backgroundColor = UIColor.green()
        
        // add pan gesture recognizer to
        circle.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.dragCircle)))
        
        self.view.addSubview(circle)
    }
    
    func dragCircle(gesture: UIPanGestureRecognizer) {
        let target = gesture.view!
        
        switch gesture.state {
        case .began, .ended:
            circleCenter = target.center
        case .changed:
            let translation = gesture.translation(in: self.view)
            target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
        default: break
        }
    }
}

There’s nothing too complicated happening here. In viewDidLoad, we created a green circle and positioned in the center of the screen. Then we attached a UIPanGestureRecognizer instance to it so that we can respond to pan events in dragCircle: by moving that circle across the screen. As you may have guessed, the result is a draggable circle:

Enjoy

About UIViewPropertyAnimator

UIViewPropertyAnimator is the main class we’ll be using to animate view properties, interrupt those animations, and change the course of animations mid-stream. Instances of UIViewPropertyAnimator maintain a collection of animations, and new animations can be attached at (almost) any time.

Note: “UIViewPropertyAnimator instance” is a bit of a mouthful, so I’ll be using the term animator for the rest of this tutorial.

If two or more animations change the same property on a view, the last animation to be added or started* “wins”. The interesting thing is than rather than an jarring shift, the new animation is combined with the old one. It’s faded in as the old animation is faded out.

* Animations which are added later to a UIViewPropertyAnimator instance, or are added with a start delay override earlier animations. Last-to-start wins.

Interruptable, Reversible Animations

Let’s dive in and add a simple animation to build upon later. With this animation, the circle will slowly expand to twice it’s original size when dragged. When released, it will shrink back to normal size.

First, let’s add properties to our class for the animator and an duration:

// ...
class ViewController: UIViewController {
    // We will attach various animations to this in response to drag events
    var circleAnimator: UIViewPropertyAnimator!
    let animationDuration = 4.0
// ...

Now we need to initialize the animator in viewDidLoad::

// ...
// animations argument is optional
circleAnimator = UIViewPropertyAnimator(duration: animationDuration, curve: .easeInOut, animations: { 
[unowned circle] in
    // 2x scale
    circle.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
})
// self.view.addSubview(circle) here
// ...

When we initialized circleAnimator, we passed in arguments for the duration and curveproperties. The curve can be set to one of four simple, predefined timing curves. In our example we choose easeInOut. The other options are easeIn, easeOut, and linear. We also passed an animations closure which doubles the size of our circle.

Now we need a way to trigger the animation. Swap in this implementation of dragCircle:. This version starts the animation when the user begins dragging the circle, and manages the animation’s direction by setting the value of circleAnimator.isReversed.

func dragCircle(gesture: UIPanGestureRecognizer) {
    let target = gesture.view!
    
    switch gesture.state {
    case .began, .ended:
        circleCenter = target.center
        
        if (circleAnimator.isRunning) {
            circleAnimator.pauseAnimation()
            circleAnimator.isReversed = gesture.state == .ended
        }
        circleAnimator.startAnimation()
        
        // Three important properties on an animator:
        print("Animator isRunning, isReversed, state: \(circleAnimator.isRunning), \(circleAnimator.isReversed), \(circleAnimator.state)")
    case .changed:
        let translation = gesture.translation(in: self.view)
        target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
    default: break
    }
}

Run this version. Try to make the circle “breathe”. Hold it down for a second..

A Sticking Point

Take a look at this video of our circle after it has made it all the way to the end of the animation:

It's not moving.

It’s not moving. It’s stuck in at the expanded size.

Ok, so what’s happening here? The short answer is that the animation threw away the reference it had to the animation when it finished.

Animators can be in one of three states:

  • inactive: the initial state, and the state the animator returns to after the animations reach an end point (transitions to active)
  • active: the state while animations are running (transitions to stopped or inactive)
  • stopped: a state the animator enters when you call the stopAnimation: method (returns to inactive)

Here it is, represented visually:

State Transitions

(source: UIViewAnimating protocol reference)

Any transition to the inactive state will cause all animations to be purged from the animator (along with the animator’s completion block, if it exists).

We’ve already seen the startAnimation method, and we’ll delve into the other two shortly.

Let’s get our circle unstuck. We need to change up the initialization of circleAnimator:

expansionAnimator = UIViewPropertyAnimator(duration: expansionDuration, curve: .easeInOut)

…and modify dragCircle::

// ...
// dragCircle:
case .began, .ended:
    circleCenter = target.center
        
    if circleAnimator.state == .active {
        // reset animator to inactive state
        circleAnimator.stopAnimation(true)
    }
    
    if (gesture.state == .began) {
        circleAnimator.addAnimations({
            target.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
        })
    } else {
        circleAnimator.addAnimations({
            target.transform = CGAffineTransform.identity
        })
    }

case .changed:
// ...

Now, whenever the user starts or stops dragging, we stop and finalize the animator (if it’s active). The animator purges the attached animation and returns to the inactive state. From there, we attach a new animation that will send our circle towards the desired end state.

A nice benefit of using transforms to change a view’s appearence is that you can reset the view’s appearance easily by setting its transform property to CGAffineTransform.identity. No need to keep track of old values.

Note that circleAnimator.stopAnimation(true) is equivalent to:

circleAnimator.stopAnimation(false) // don't finish (stay in stopped state)
circleAnimator.finishAnimation(at: .current) // set view's actual properties to animated values at this moment

The finishAnimationAt: method takes a UIViewAnimatingPosition value. If we pass start or end, the circle will instantly transform to the scale it should have at the beginning or end of the animation, respectively.

About Durations

There’s a subtle bug in this version. The problem is, every time we stop an animation and start a new one, the new animation will take 4.0 seconds to complete, no matter how close the view is to reaching the end goal.

Here’s how we can fix it:

// dragCircle:
// ...
case .began, .ended:
    circleCenter = target.center
    
    let durationFactor = circleAnimator.fractionComplete // Multiplier for original duration
    // multiplier for original duration that will be used for new duration
    circleAnimator.stopAnimation(false)
    circleAnimator.finishAnimation(at: .current)
    
    if (gesture.state == .began) {
        circleAnimator.addAnimations({
            target.backgroundColor = UIColor.green()
            target.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
        })
    } else {
        circleAnimator.addAnimations({
            target.backgroundColor = UIColor.green()
            target.transform = CGAffineTransform.identity
        })
    }
    
    circleAnimator.startAnimation()
    circleAnimator.pauseAnimation()
    // set duration factor to change remaining time
    circleAnimator.continueAnimation(withTimingParameters: nil, durationFactor: durationFactor)
case .changed:
// ...

Now, we explicitly stop the animator, attach one of two animations depending on the direction, and restart the animator, using continueAnimationWithTimingParameters:durationFactor:to adjust the remaining duration. This is so that “deflating” from a short expansion does not take the full duration of the original animation. The method continueAnimationWithTimingParameters:durationFactor:can also be used to change an animator’s timing function on the fly*.

* When you pass in a new timing function, the transition from the old timing function is interpolated. If you go from a springy timing function to a linear one, for example, the animations may remain “bouncy” for a moment, before smoothing out.

Timing Functions

The new timing functions are much better than what we had before.

The old UIViewAnimationCurve options are still available (static curves like easeInOut, which I’ve used above), and there are two new timing objects available: UISpringTimingParameters and UICubicTimingParameters

UISpringTimingParameters

UISpringTimingParameters instances are configured with a damping ratio, and an optional mass, stiffness, and initial velocity. These are all fed into the proper equation to give you realistically bouncy animations. The initializer for your view property animator will still expect a duration argument when passed an instance of UISpringTimingParameters, but that argument is ignored. The equation doesn’t have a place for it. This addresses a complaint about some of the old spring animation functions.

Let’s do something different and use a spring animator to keep the circle tethered to the center of the screen:

ViewController.swift

import UIKit

class ViewController: UIViewController {
    // this records our circle's center for use as an offset while dragging
    var circleCenter: CGPoint!
    // We will attach various animations to this in response to drag events
    var circleAnimator: UIViewPropertyAnimator?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Add a draggable view
        let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
        circle.center = self.view.center
        circle.layer.cornerRadius = 50.0
        circle.backgroundColor = UIColor.green()
        
        // add pan gesture recognizer to circle
        circle.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.dragCircle)))
        
        self.view.addSubview(circle)
    }
    
    func dragCircle(gesture: UIPanGestureRecognizer) {
        let target = gesture.view!
        
        switch gesture.state {
        case .began:
             if circleAnimator != nil && circleAnimator!.isRunning {
                circleAnimator!.stopAnimation(false)
            }
            circleCenter = target.center
        case .changed:
            let translation = gesture.translation(in: self.view)
            target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
        case .ended:
            let v = gesture.velocity(in: target)
            // 500 is an arbitrary value that looked pretty good, you may want to base this on device resolution or view size.
            // The y component of the velocity is usually ignored, but is used when animating the center of a view
            let velocity = CGVector(dx: v.x / 500, dy: v.y / 500)
            let springParameters = UISpringTimingParameters(mass: 2.5, stiffness: 70, damping: 55, initialVelocity: velocity)
            circleAnimator = UIViewPropertyAnimator(duration: 0.0, timingParameters: springParameters)
            
            circleAnimator!.addAnimations({
                target.center = self.view.center
            })
            circleAnimator!.startAnimation()
        default: break
        }
    }
}

Drag the circle and let it go. Not only will it bounce back to the starting point. it will even keep the momentum it had when you released, since we passed a velocity argument to initialVelocity: when we initialized the spring timing parameters:

// dragCircle: .ended:
// ...
let velocity = CGVector(dx: v.x / 500, dy: v.y / 500)
let springParameters = UISpringTimingParameters(mass: 2.5, stiffness: 70, damping: 55, initialVelocity: velocity)
circleAnimator = UIViewPropertyAnimator(duration: 0.0, timingParameters: springParameters)
// ...
Springy

At an interval, I drew a small circle at our main circle’s center point in order to trace the animation path for this screenshot. The “straight” lines curve a little, because some momentum was retained as the circle was released and pulled inward by the spring.

UICubicTimingParameters

UICubicTimingParameters allows you to set control points to define a custom cubic Bézier curve. Just note that coordinate points outside of 0.0 – 1.0 are trimmed to that range:

// Same as setting the y arguments to 0.0 and 1.0 respectively
let curveProvider = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.2, y: -0.48), controlPoint2: CGPoint(x: 0.79, y: 1.41))
expansionAnimator = UIViewPropertyAnimator(duration: expansionDuration, timingParameters: curveProvider)

If you’re not happy with those timing curve providers, you can implement and use your own by conforming to the UITimingCurveProvider protocol.

Animation Scrubbing

You can manually set the progress of an paused animation by passing a value between 0.0 and 1.0* to your animator’s fractionComplete property. A value of 0.5, for example, will place the animatable properties halfway towards their final value, regardless of timing curve. Note that the position you set is mapped to the timing curve when you restart an animation, so a fractionComplete of 0.5 does not necessarily mean the remaining duration will be half of the original duration.

Let’s try out a different example. First, let’s initialize our animator at the bottom of viewDidLoad: and pass in two animations:

// viewDidLoad:
// ...
circleAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear, animations: {
    circle.transform = CGAffineTransform(scaleX: 3.0, y: 3.0)
})
    
circleAnimator?.addAnimations({ 
    circle.backgroundColor = UIColor.blue()
}, delayFactor: 0.75)
// ...

We aren’t going to call startAnimation this time. The circle should get larger as the animation progresses and start turning blue at 75%.

We need a new dragCircle: implementation as well:

func dragCircle(gesture: UIPanGestureRecognizer) {
    let target = gesture.view!
    
    switch gesture.state {
    case .began:
        circleCenter = target.center
    case .changed:
        let translation = gesture.translation(in: self.view)
        target.center = CGPoint(x: circleCenter!.x + translation.x, y: circleCenter!.y + translation.y)
        
        circleAnimator?.fractionComplete = target.center.y / self.view.frame.height
    default: break
    }
}

Now we’re updating the animator’s fractionComplete to the circle’s vertical position on the view as it’s dragged:

Rev-3 Rev-4

I’ve used the linear timing curve, but this sample would be a good way to get a feel for other curves or a timing curve provider instance. The animation that changes the circle blue follows a compressed version of the animator’s timing curve.

* Custom animators can accept value outside of range 0.0 – 1.0, if they support animations going past their endpoints.

Extensibility

Finally, one of the most interesting things about this release was the apparent philosophy behind it. Don’t like something? Change it. Animation providers and timing curves providers can both be replaced with your own implementation, for example.

In fact, this is almost more of a release of protocols than classes. The underlying protocols got about as much fanfare as everything else, which is great. I love this direction of making more and more built-in functionality accessible to the developer. I’m hoping we see more in the future, and I’m looking forward to seeing what people do with this.


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

Subscribe via RSS

SiriKit Resolutions with Swift 3 and iOS 10 – SiriKit Tutorial (Part 2)

SiriKit Resolutions with Swift 3 in iOS 10 – SiriKit Tutorial (Part 2)

This tutorial written on June 20th, 2016 using the Xcode 8 Beta 1, and is using the Swift 3.0 toolchain.

This post is a follow-up in a multi-part SiriKit tutorial. If you have not read part 1 yet, I recommend starting there.

Resolving requests from SiriKit

In order to make our Siri integration more useful, we can help fill out the content of our message using a callback method from the INSendMessageIntentHandling protocol. Investigating this protocol you can see this show up an optional methods.

resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: ([INPersonResolutionResult]) -> Swift.Void)
 
resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Swift.Void)
 
resolveGroupName(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Swift.Void)
 
resolveServiceName(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Swift.Void)
 
resolveSender(forSendMessage intent: INSendMessageIntent, with completion: (INPersonResolutionResult) -> Swift.Void)

So we can provide SiriKit with further information by implementing as many of these resolutions as we wish. Effectively enabling us to provide information regarding the recipients, content, group name, service name, or sender. These should be relatively self-explanatory.

Let’s try providing some static data for our title and content, to demonstrate how resolutions work.

First, let’s add the resolution for the content of the message, by implementing the resolveContent protocol method.

func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Void) {
    let message = "My message body!"
    let response = INStringResolutionResult.success(with: message)
    completion(response)
}

Here we create a string resolution result, and call the success function. This is the simplest way to proceed, but we also have the option of returning a disambiguation, confirmationRequired, or unsupported response. We’ll get to those later, but first let’s actually use the data Siri is providing us.

Siri will send in it’s own transcription of our message in the intent object. We’re interested in the content property, so let’s take that and embed it inside of a string.

func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Void) {
    let message = "Dictated text: \(content!)"
    let response = INStringResolutionResult.success(with: message)
 
    completion(response)
}

The content property is an optional, and as such we need to make sure Siri actually provided a transcription. If no transcription was provided then a message won’t be entirely useful, so we need to tell Siri that the information is missing and we need this value. We can do this by returning a resolution result calling the needsValue class method on INStringResolutionResult.

func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: (INStringResolutionResult) -> Void) {
    if let content = intent.content {
        let message = "Dictated text: \(content)"
        let response = INStringResolutionResult.success(with: message)
        completion(response)
    }
    else {
        let response = INStringResolutionResult.needsValue()
        completion(response)
    }
}

SiriKit requesting additional information

Now SiriKit knows when we try to send a message, that the content value is a requirement. We should implement the same type of thing for the recipients. In this case, recipients can have multiple values, and we can look them up in a variety of ways. If you have a messaging app, you would need to take the INPerson intent object that is passed in and try to determine which of your own user’s the message is intended for.

This goes outside the scope of this Siri tutorial, so I’ll leave it up to you to implement your own application logic for the resolveRecipients method. If you want to see an example implementation, Apple have released some sample code here.

More iOS 10 Tutorials

We’ll be continuing to investigate iOS 10 and publish more free tutorials in the future. If you want to follow along be sure to subscribe to our newsletter and follow me on Twitter.

Thanks,
Jameson


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

Subscribe via RSS

Creating an iMessage Sticker App on iOS 10 with Swift – Tutorial (Part 1)

Creating iMessage Apps with XCode 8

This is part of a series of tutorials introducing new features in iOS 10, the Swift programming language, and the new XCode 8 beta, which were just announced at WWDC 16

Intro

Among the most exciting additions in iOS 10 are some of the new app extension types, and the new extension type that really caught my attention was the iMessage app, which allow you to add functionality to the built-in Messages app on iOS.

Unlike other extension types, which requires you to bundle your extension with a standard app, iMessage apps can be released as standalone apps through the the new iMessage App Store, which will be accessible from within the Messages app. While you can include your Messages app extension as part of a standalone app, with this new branch of the app store, for the first time, you don’t have to.

When users use your iMessage app to communicate with people who don’t have it, the recipient will be prompted with a link to download it from the store.

The purpose of this tutorial is to create a quick, searchable guide for creating a sticker app (which you can do without writing a line of code), and very briefly introduce the new Messages framework (Messages.framework) for building interactive apps.

Creating Content and Apps for iMessage

iMessage Sticker Apps

There’s a very easy option for artists (even non-programmers) who want to release content for the iMessage app store. You can offer sticker apps: purchasable packs of stickers (even animated stickers) that users can “peel” (using a long press) and slap onto any balloon in a conversation. Like all iMessage apps, these packs can be a bonus bundled with your existing app, or a standalone package offered for sale. Apart from creating the assets, releasing a sticker app couldn’t be much easier.

Still in Beta

A sticker pane, with a sick computer stuck onto a message

The sticker packs themselves don’t require any code to create. All you really need is a copy of XCode 8 and an Apple Developer account. The few simple guidelines for creating the assets can be found under “Sticker Packs” in the Messages Framework reference. For convenience, I’ve copied a slightly-edited version here.

Each sticker image should meet this criteria:

  • The image must be a PNG, APNG, GIF or JPEG file.
  • The file must be less than 500KB.
  • The image must be between 100×100 and 206×206 points

PNG or APNG (Animated PNG) will generally look best. Make sure your stickers look good against different color backgrounds; they can be applied to photos.

Apple uses points (rather than pixels) for screen coordinates and dimensions on their devices. 10×10 points (pt) @ 2x equals 20×20 pixels (px). The original retina display crammed 4 pixels into the space of one (1×1 became 2×2). Simply by providing ‘@2x’ copies of images at that higher resolution, developers were able to quickly update their old apps to support the sharper display.

Always provide @3x images – between 300×300 and 618×618 pixels. The system generates the @2x and @1x versions by downscaling the @3x images at runtime.

In Xcode’s Attributes inspector, set the Sticker Size for the entire Sticker pack. The system lays out the stickers in the browser based on these sizes. To best match the browser, use sticker images of the specified size. The Sticker Size defaults to Medium.

The recommended maximum dimensions for each size setting are:

  • Small: 100x100pts.
  • Medium: 136x136pts.
  • Large: 206x206pts.

Attributes Inspector

Xcode’s Attributes Inspector after selecting the “Sticker Pack” group in Stickers.xcstickers

Building the App

Start a new project in XCode, and you’ll see there’s a new template for creating a standalone sticker pack.

Sticker Pack Template

You’re almost done. Select Stickers.xcstickers and add some icons so that your sticker pack looks good in Messages and on the store, then drag your stickers into the “Sticker Pack” group.

Note: You can create animated (APNG) stickers with just XCode. Right click inside your sticker pack group and select Add Assets -> New Sticker Sequence. Use the attributes inspector on the right to set options like number of frames, framerate and repetition count.

Sticker Pack Template

Note: If the app won’t run, try the following:

  • Select your “Run” build scheme, ensure that ‘Executable’ is set to [projectname].app (stickers.app in my case)
  • Provide images for at least the 1x versions of the 27×20, 32×24, and 1024×768 point icons, as well as the 2x icon for “Settings” and “Messages” for your target platform
  • Note that 2x means to double the requested dimensions (2x for 29x29pt means provide a 58×58 px image)

If you are ramping up to release your app, make sure to provide images for all icon versions and sizes.

The new version of the simulator for iOS 10 includes a special version of the Messages app which allows you to see both sides of a conversation as you test your iMessage apps.

My sticker app (containing an animated gif and the obligatory cat photo) may not win any awards, but it works. Here is is in action, with the sticker tray open and a few stickers strategically applied:

AAAAAAHHHHH!!

If you’ve made it this far (and provided more/better art) your app is now ready for submission to iTunes Connect and Apple’s review process (pending availability of the release version of XCode 8).

Customizing the Sticker Browser

Developers should be aware that it is possible to customize the appearance of the sticker browser (the window that users peel stickers from) in a full-fledged iMessage app by subclassing MSStickerBrowserViewController. You can also load stickers dynamically by setting your StickerBrowserViewController’s data source to a class which conforms to MSStickerBrowserViewDataSource.

Developing Interactive iMessage Apps

There’s a lot more you can do with iMessage Apps. For now, I just want to briefly discuss Messages.framework. I hope to dive much deeper in the near future.

Note: There is one major limitation that may kill your idea. You won’t be allowed to read the content of user’s messages or even, necessarily, know anything about the participants in the session. Apple takes privacy very seriously; this is as it should be.

A number of the built-in apps provide good examples. Apart from the sticker features, the new drawing capabilities, the music sharing app and the Bing image search, in particular, are good examples of the type of thing that third-party developers could realistically build today.

There is a new, relatively bare-bones, project template in XCode 8: “Messages Application”. MessagesViewController a subclass of MSMessagesAppViewController is your main entry point and does at least provide a number of comments. This is the view controller behind your app’s view in the app drawer and when expanded.

The documentation on these classes is there for anyone getting started and a helpful introduction and associated sample project are now available.


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

Subscribe via RSS

Siri Integration in iOS 10 with Swift – SiriKit Tutorial (Part 1)

Siri integration on iOS 10 – Swift Tutorial

This tutorial written on June 13th, 2016 using the Xcode 8 Beta 1, and is using the Swift 3.0 toolchain.

Get Xcode 8 set up for iOS 10 and Swift 3 compilation.

If you have not yet downloaded Xcode 8 Beta 1, please do so here.

(Optional) Compiling from the command line

To opt in to the Swift 3.0 toolchain you shouldn’t need to change anything unless you want to build from the command line. If you plan to build from the command line, open Xcode-beta and from the OS menu bar select Xcode > Preferences. Then select the Locations tab. At the bottom of the page here you will see “Command Line Tools”. Make sure this is set to Xcode 8.0.

Now if you navigate to the project directory containing the .xcodeproj file, you can optional compile your project by calling xcodebuild from the command line.

(Optional) Migrating from an existing Swift 2 app

If you are working with an existing Swift 2 project and want to add Siri integration with Swift 3.0, click on the root of your project and select Build Settings. Under Swift Compiler – Version, find the field labeled Use Legacy Swift Language Version and set it to No. This will lead to compiler errors most likely that you will need to fix throughout your project, but it’s a step I recommend to keep up with Swift’s ever-changing semantics.

Getting started with SiriKit

First, in your app (or in a new single-view Swift app template if you are starting fresh), switch to the general view by selecting the root of your project. Under this tab you can click the (+) icon in the lower land corner of the side-pane on the left. From the dropdown that appears selection iOS > Application Extension, and then select Intents Extension.

Select Intents Extension

This adds a new intent to the project, and we’ll use it to listen for Siri commands. The product name should be something similar to your app so it’s easy to identify, for example if your app is called MusicMatcher, you could call the Product Name of this intent MusicMatcherSiriIntent. Make sure to also check the checkbox to Include UI Extension. We will need this later in the tutorial, and it’s easiest to just include the additional extension now.

Intents Extension Options

What we’ve created are two new targets as you can see in the project heirarchy. Let’s jump in to the boilerplate code and take a look at the example in the IntentHandler.swift file inside of the Intent extension folder. By default this will be populated with some sample code for the workout intent, allowing a user to say commands such as “Start my workout using MusicMatcher”, where MusicMatcher is the name of our app.

IntentHandler.swift

Run the Template App as-is

It’s helpful at this point to compile this code as-is and try out the command on an actual iOS device. So go ahead and build the app target, by selecting the app MusicMatcher from the Scheme dropdown, and when the target device set to your test iOS device, press the Build & Run button.

Select the MusicMatcher target

You should see a blank app appear, and in the background your extensions will also be loaded in to the device’s file system. Now you can close your app using the Stop button in Xcode to kill the app.

Then, switch your scheme to select the Intent target, and press build & run again.

Select the MusicMatcherSiriIntent target

This will now prompt asking which app to attach to, just select the app you just ran, MusicMatcher. This will present the app again on your device (a white screen/blank app most likely), but this time the debugger will be attached to the Intent extension.

Select the app to run with the extension

You can now exit to the home screen by pressing the home button, or the app may exit on it’s own since you are running the Intent and not the app itself (This is not a crash!!!)

Enable the extension

The extension should now be in place, but we as an iOS device user still may need to enable the extension in our Siri settings. On your test device enter the Settings app. Select the Siri menu, and near the bottom you should see MusicMatcher listed as a Siri App. Make sure the app is enabled in order to enable Siri to pick up the intents from the sample app.

Testing our first Siri command!

Try the Siri command. Activate Siri either by long pressing the Home button, or by saying “Hey Siri!” (note the “Hey Siri!” feature must be enabled in the settings first)

Try out some of the command “Start my workout using MusicMatcher”.

“Sorry, you’ll need to continue in the app.”

If you’re like me this will bail with an error saying “Sorry, you’ll need to continue in the app.” (For some reason this occassionally was not a problem. Ghosts?)

In the console you may see something like this:

dyld: Library not loaded: @rpath/libswiftCoreLocation.dylib
  Referenced from: /private/var/containers/Bundle/Application/CC815FA3-EB04-4322-B2BB-8E3F960681A0/LockScreenWidgets.app/PlugIns/JQIntentWithUI.appex/JQIntentWithUI
  Reason: image not found
Program ended with exit code: 1

We need to add the CoreLocation library to our main project, to make sure it gets copied in with our compiled Swift code.

Select the project root again and then select your main MusicMatcher target. Here under General you’ll find area area for Linked Frameworks and Libraries. Click the (+) symbol and add CoreLocation.framework. Now you can rebuild and run your app on the device, then follow the same steps as above and rebuild and run your intent target.

Finally, you can activate Siri again from your home screen.

“Hey Siri!”
“Start my workout using MusicMatcher”

Siri should finally respond, “OK. exercise started on MusicMatcher” and a UI will appear saying “Workout Started”

MusicMatcher Workout Started

How does it work?

The IntentHandler class defined in the template uses a laundry list of protocols:

First an foremost is INExtension which is what allows us to use the class as an intent extension in the first place. The remaining protocols are all intent handler types that we want to get callbacks for in our class:

INStartWorkoutIntentHandling
INPauseWorkoutIntentHandling
INResumeWorkoutIntentHandling
INCancelWorkoutIntentHandling
INEndWorkoutIntentHandling

The first one is the one we just tested, INStartWorkoutIntentHandling.

If you command-click this protocol name you’ll see in the Apple docs this documentation:

/*!
 @brief Protocol to declare support for handling an INStartWorkoutIntent
 @abstract By implementing this protocol, a class can provide logic for resolving, confirming and handling the intent.
 @discussion The minimum requirement for an implementing class is that it should be able to handle the intent. The resolution and confirmation methods are optional. The handling method is always called last, after resolving and confirming the intent.
 */

Or in other words, this protocol tells SiriKit that we’re prepared to handle the English phrase “Start my workout with AppName Here.”
This will vary based on the language spoken by the user, but the intent will always be to start a workout. The INStartWorkoutIntentHandling protocol calls on several more methods, and they are implemented in the sample code. I’ll leave you to learn more about them if you want to build a workout app, but what I’d rather do in the remainder of this tutorial is add a new intent handler for handling the sending of messages.

Let’s Add a New Message Intent

Now that we’ve confirmed that works, let’s move on to adding a new type of intent for sending messages. The docs here show the following:

Send a message
Handler:INSendMessageIntentHandling protocol
Intent:INSendMessageIntent
Response:INSendMessageIntentResponse

So let’s add the INSendMessageIntentHandling protocol to our class. First we’ll just specify we want to use it by appending it to the list of protocols our class adheres to in IntentHandler.swift. Since I don’t actually want the workout intent’s, I’ll also remove those, leaving us with just this for the class declaration:

class IntentHandler: INExtension, INSendMessageIntentHandling {
    ...

If we just left it at that we wouldn’t be able to compile our code since we stil need to implement the required methods from the INSendMessageIntentHandling protocol.

Again, if you ever need to check what those methods are, just command+click the text INSendMessageIntentHandling and take a look at what method signatures are present that are not marked with the optional keyword.

In this case we find only one required method:

/*!
 @brief handling method
 
 @abstract Execute the task represented by the INSendMessageIntent that's passed in
 @discussion This method is called to actually execute the intent. The app must return a response for this intent.
 
 @param  sendMessageIntent The input intent
 @param  completion The response handling block takes a INSendMessageIntentResponse containing the details of the result of having executed the intent
 
 @see  INSendMessageIntentResponse
 */
public func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Swift.Void)

Adhering to the new Message Intent protocol

So back in our IntentHandler.swift, let’s add a line seperator (useful for navigating code with the jump bar)

// MARK: - INSendMessageIntentHandling

Underneath this MARK, we can implement the function. I find it’s most useful with Xcode 8 to simply begin typing the method name, and let autocomplete take it from there, choosing the relevant option.

Fill out the handle method with autocomplete

In our handler, we’ll need to construct an INSendMessageIntentResponse in order to call back the completion handler. We’ll just assume all messages are successful here and return a success value for the user activity in the INSendMessageIntentResponse constructor, similar to how this is done in the template app. We’ll also add a print statement so we can see when this handle method is triggered by a Siri event:

func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
    print("Message intent is being handled.")
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent))
    let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity)
    completion(response)
}

Adding the intent type to the Info.plist

Before this app will be capable of handling INSendMessageIntent, we need to add the value to our Info.plist. Think of this as something like an app entitlement.

In the Info.plist file of the intent, find and expand the NSExtension key. Then extend NSExtensionAttributes, and then IntentsSupported under that. Here we need to add a new row for our INSendMessageIntent to allow the app to process Message intents.

Add support for INSendMessageIntent aka messages intent

Testing the new intent

Now that we’ve got our new intent set up, let’s give it a try. Recall that you must build the app, run it on the device, and then run the extension in order to debug the extension. If you don’t do run in this order the extension will either not work, or it will not log to the Xcode console.

Try calling upon our intent in Siri, and you will now see a new message window appear! The window is pretty empty, and there isn’t much logic to tie in to our app just yet. We need to implement the remaining callbacks and add some of our app’s messaging logic to provide a better experience. We’ll cover that in Part 2, which is available now. If you want me to email you about it when other tutorials come out as well, sign up for my newsletter to get the scoop.


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

Subscribe via RSS

How To Migrate Your App And Protect It From Shutdowns Like Parse’s

How To Migrate Your App And Protect It From Shutdowns Like Parse’s


Editor’s Note: This article is written by Reinder de Vries, indie app developer at LearnAppMaking.com.

Oh no! Parse is shutting down… How do you move on?

On January 28th Parse announced it will be shutting down its service. They’re winding down the done-for-you Backend-as-a-Service, fully retiring it on January 28, 2017.

That’s bad news for over 500.000 developers who relied on the service since 2011. Like Jameson argued in his recent blog post, developers are feeling betrayed in their trust.

Parse was acquired in 2013 by Facebook for $ 85 million and now the “Big Five” monarch cold-bloodedly pulls the plug on the backbone of many developer’s apps.

Back in 2013 Parse was a risky acquisition, which Facebook needed to get a foothold in the mobile market. Facebook’s recent earnings report shows a growth in mobile advertising revenue of 52%, comparing Q4 of 2014 and 2015, which clearly shows they’ve landed in mobile.

With the rise in competition of cloud services by the likes of Amazon, Microsoft and Google, you could argue Facebook doesn’t need Parse anymore. From a business perspective I couldn’t agree more with them, but from a developer’s perspective it’s an awful move to make.

Or is it?

Parse’s Shutdown Is An Opportunity For App Developers

Before I built apps, I built websites. Lots and lots of websites. I learned all the necessary skills: HTML, CSS, JavaScript, PHP and MySQL. The quality and elegance of my code back then would make your skin crawl, but through trial and error I eventually mastered delivering quality websites.

Throughout the years I kept my craft up-to-date by building the occasional web app. Newer and more advanced tools became available and my job required I use them. I’ve transitioned from jQuery to Angular to React, touched on too many JavaScript frameworks in between, saw the rise of package managers and GitHub (SourceForge, anyone?), the switch from PHP 4 to PHP 5 to PHP 5.6 and the upcoming PHP 7. My MySQL tables exploded, so I switched to Elasticsearch and Mongo.

A while back I wanted to automate email newsletter signups and TestFlight beta invites. If you’d subscribe to the newsletter and confirmed your subscription, the workflow would automatically add you to my TestFlight pool of beta testers. I knew about the awesome FastLane toolset, and its boarding component, and figured boarding‘s HTML form must have a POST URL endpoint I could hijack. Essentially, I’d catch the subscriber’s email address from my form and input it into the boarding form. Boarding would take care of the rest, like adding the subscriber to the TestFlight beta tester pool.

There was only one problem: the boarding form had CSRF protection, which meant it expected a unique token to be included in every request. Those tokens are created server-side and in my workflow I didn’t have access to them.

I had access to boarding‘s source code, so technically I could solve the problem by disabling the CSRF protection. There was only one caveat: the source code was in Ruby, and I haven’t seen a single line of Ruby in my entire life (OK, maybe one), let alone have written one.

Thanks to my extensive background in back-end programming, I’m not afraid of getting my hands dirty with technology I don’t know. Years of tinkering and making mistakes gave me a perfect understanding of how the web works. Instead of shying away from a black box, I dug deeper.

I saw libraries I’d never heard of, with even stranger names (is this a thing?), and finally found the file that governed the CSRF protection feature. Ultimately, turning it off simply meant “nulling” a controller.

The Back-End Black Box

Almost every successful app uses some sort of back-end service to connect app users to each other, and persist user data on a server.

Many developers I meet and coach don’t have a solid understanding of back-end programming. Once they set up their Parse configuration in the overly simple Dashboard, write a query, they just assume their data is “out there”. Why would you bother finding out what happens in the cloud?

I often explain Parse as “a spreadsheet for your app”. It’s true: the Parse Dashboard isn’t more complicated than Google Spreadsheets, yet it powers the entire back-end of your app. You don’t even have to write queries for data, instead you use convenience methods like:

PFQuery(className: "Tweet").whereKey("user", currentUser);

It’s perhaps also Parse’s fault. Their PFQueryTableViewController is so incredibly easy to use, but pretty dangerous: it directly couples back-end code with your app’s view controller (which is already a ViewModel). Even if you wanted to switch from Parse to Firebase, your code is so tightly coupled you have to rewrite your entire app before you can migrate.

Parse’s PFObject class has mapping built in, so you can work with string data as native Swift String objects, while in fact the Parse SDK turns that into data it can send over the internet. If you pass around PFObject instances in your user-facing code, you’re going to have a hard time replacing that with a new Backend-as-a-Service SDK.

If you would have built your own back-end from scratch, you would have known for instance that true (a boolean) is sometimes turned into "true" (a string) when encoded as JSON. If that hits your app, you’re suddenly working with an instance of String instead of the Bool you expected. Fortunately Swift has an incredible type system, but an if let optional binding statement only protects against a type mismatch — it doesn’t resolve it!

So how’s this Parse shutdown good for you? It’s time to start tinkering again, to dig deeper into the black box and to protect your code against shutdowns like Parse’s.

3 Ways To Make Your App Shutdown Proof

Next time you build an app, keep these three heuristics in mind. Instead of relying for 100% on one provider whose service is vital to the continuity and success of your app, you can protect yourself against shutdowns.

1. Make a list of alternatives
Parse has a couple of similar providers, like Firebase. Perhaps you didn’t even know Firebase existed until you Googled for alternatives in reaction to Parse’s shutdown.

The cool thing is: most of these services know you’re looking for them. If you Google “parse shutdown” or “parse migration”, you’re hit with multiple highly relevant Google AdWords ads that tell you to migrate over to their service. Don’t do it!

Instead, make a list of alternatives. Just make yourself comfortable with what’s out there. It’s never a bad idea to have a list of providers you rely on, and their alternatives. Just like a supermarket has a list of alternative milk suppliers in case their main provider goes out of business, you should keep a list of companies you do business with.

You can of course also use the list of alternatives to do some proper research about the technologies and SDKs you’re going to use. I didn’t know OneSignal existed until I started looking for a free push provider, to replace Urban Airship, because I found it too expensive.

2. Decouple your code
Regardless of how dull it may sound: Model-View-Controller still has a point. We’re getting used to coding Model-View-Whatever, but the principle of MVC remains: your Controller shouldn’t configure Views, and your View shouldn’t manipulate a Model.

Cocoa Touch makes it hard to not use MVVM (Model-View-ViewModel), but that doesn’t mean you should tightly couple your Model code in your View Controller code.

In programming theory we’ve got two principles: coupling and encapsulation. Working with two tightly coupled classes means ClassA makes a lot of assumptions about the inner functionality of ClassB.

Imagine a car and a driver. The driver sits in the front of the car, because the steering column is directly connected to the engine. What if the driver suddenly decided to sit in the back, in order to drive the car? Then the driving doesn’t work. Of course, such a situation never happens in real life, but it happens in your code all the time.

A protection against tight coupling is encapsulation. This means every class has a strictly defined set of methods, such as speedUp() and steerLeft(). Instead of letting the driver inject more gas into the combustion chamber, relying on the engine being of the combustible kind, you simply let the driver call speedUp(). This way the driver doesn’t have intricate knowledge about the workings of the engine, but instead calls upon a function he knows is safe.

When the engine gets replaced by an electric one, which doesn’t have a combustion chamber, you don’t have to change the driver’s code: he relies on a speedUp() method to be present, instead of relying on a injectGasMixture(), gasMixture and sparkTimer.

If you find the principle of decoupling and encapsulation hard to grasp, think of it as responsibilities. Whose responsibility is it to drive the car? The driver’s. Who’s responsibility is it to make the car able to move forward? The engine’s.

When you’re coding your back-end enabled app, make sure you clearly define its components and their responsibilities. Create a class you only use as an intermediary between back-end code and front-end code. The front-end code is based on your own native classes, like Car, and the back-end code has intricate knowledge about the back-end SDK you use, and works with classes like PFObject.

It’s the intermediary’s job to communicate between the front-end and the back-end. In case your back-end code becomes obsolete, you can replace the intermediary with code that understands your new back-end SDK, without rewriting the front-end code as well.

3. Use the Adapter Pattern
The third and last heuristic ties into the principle of loosely coupled code. The Adapter Pattern, older than the internet itself, is an Object-Oriented Programming pattern. It’s a set of programming design rules you can use to make two incompatible classes compatible with each other.

It’s similar to making a “wrapper”, but instead of just exposing the interface of one resource (like wrapping a REST API) it also mediates between two incompatible interfaces by defining a set of rules. In many programming languages these rules are called interfaces (Java and PHP), in Swift and Objective-C they’re called protocols.

You do know Swift’s generics, right? If it swims like a duck and quacks like a duck, it must be a duck. The Adapter Pattern has a similar mechanism. Instead of integrating ClassA and ClassB tightly with each other, by making direct calls from A to B, you build an adaptor class around ClassA.

The adapter class has intricate knowledge about how ClassA works. The adapter class also conforms to a protocol. The protocol says: “You can be sure I have methods X, Y and Z”.

ClassB doesn’t know ClassA. It only knows it wants to work with an instance that conforms to the adapter’s protocol. It doesn’t care if the instance is a duck, as long as it swims and quacks like a duck.

What’s the benefit of working with an adapter? Well, in case your original ClassA code becomes obsolete, you can just find another class (i.e. another Parse alternative) and write an adapter for it. As long as this adapter conforms to the protocol, the rules you defined earlier, ClassB still works.

In reality, you would write an adapter (MyTwitterAdapter) that has a method with this signature:

class MyTwitterAdapter:AnyTwitterServiceProtocol
{
    func getTweetsWithUser(user:User, withPage page:Int = 0)
    {
        ...

The protocol (AnyTwitterServiceProtocol) contains the exact same method signature, but it doesn’t have any logic or method implementation. It just sets the rules.

protocol AnyTwitterServiceProtocol
{
    func getTweetsWithUser(user:User, withPage page:Int = 0);
    ...

The front-end code (MyTweetsViewController) has a variable twitter of an undefined type, which it got from the “adapter factory”. This factory ensures you only have to replace class names once (or twice) if you change adapters.

If you’d strictly define twitter to be of type MyTwitterAdapter, you would still tightly couple your code, most likely in more than one place. Then, if you replace your adapter for a new one, you still have to update a lot of code.

Once you’ve got this set up, you can test if the twitter instance you’re working with conforms to the AnyTwitterServiceProtocol.

if let _twitter = twitter as? AnyTwitterServiceProtocol
{
    // Yes, it conforms
}

Your front-end code doesn’t care anymore whether twitter is of type Car, FooBar or Facebook, as long as it conforms to AnyTwitterServiceProtocol. It simply wants to know if the instance has a method called getTweetsWithUser:withPage:.

Note: in case you do want to strictly define the type of twitter, you can use a protocol as a type, and you can also work with generics.

What’s Next?

Now that you’ve learned that you should improve your business operations and your reliance on suppliers, and protect your code against sudden changes, you can figure out how to do that.

Are you going to switch to Firebase? You should know that Firebase is owned by “Big Five” Google, and although Parse’s shutdown is no harbinger of more shutdowns, solely relying on a new provider leaves you equaly vulnerable as you were. Unless you protect your code, of course.

A good exercise would be to learn about back-end programming. Parse has released an excellent replacement product, Parse Server, which you can run on your own server (as long as it can run NodeJS and MongoDB). In the following code guide I’ll show you how to migrate a simple app from Parse to Parse Server, with Heroku and MongoLab.

Continue: Guide: How To Migrate Your Parse App To Parse Server With Heroku And MongoLab

Another relatively easy exercise is to migrate your Push Notifications to two services instead of one. Implementing the client-side SDK of a push provider is relatively easy, usually you only have to send over the unique push ID and set up a segment.

Use the Adapter Pattern to create a unified interface your front-end code can talk to. Decouple the actual adapter from the front-end code by defining a protocol. Pick a second Push Notifications provider (like Urban Airship, Mixpanel, Pusher or PushOver) and write a second adapter. Make sure it conforms to the protocol, and then simulate a service breakdown in which you have to deploy your new adapter. Does it work?

Conclusion

Don’t be afraid to go deep into the black box. It’s not a bad idea to write your own back-end REST API, just for the fun and experience of it. You’ll learn a lot.

Did you know Crashlytics is owned by Twitter? Anything could shutdown… Make sure you know what Crashlytics does for you, and how it works. It’s the only way to build a stable, reliable and long-term profitable business.

Down the rabbit hole you go.

About the author

Reinder de Vries is an indie app maker who teaches aspiring app developers and marketers how to build their own apps at LearnAppMaking.com. He has developed 50+ apps and his code is used by millions of users all over the globe. When he’s not coding, he enjoys strong espresso and traveling.


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

Subscribe via RSS

Adding IBInspectable to a superclass in Swift

Adding IBInspectable to a UICollectionViewCell subclass

One very useful feature I find is to add a IBInspectable background image to my views. In an entertainment app I’m currently building there is a designable cell that looks like this:

@IBDesignable
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}

what’s great about this is it gives me a very easy way to set a background image on my TrailerCollectionViewCell. It shows right up in my storyboards so I can easily set it to my “play” icon graphic.

IBInspectable in Xcode 7

Notice on the top-right of the attributes inspector it adds a “Bg Image” dropdown. When this is changed the didSet method above is triggered and it updates the view inside of Xcode, and when the iOS app runs. This is happening because the decoding from interface builder will also trigger didSet in the same fashion that interface builder does.

What’s not so handy about this approach is that I have two other types of CollectionViewCell subclasses that need similar functionality. So, I have these three classes inherit from one superclass that implements the functionality.

import Foundation
import UIKit
 
class MediaCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    var imgView = UIImageView()
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}
 
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
}
 
class ReviewCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var ratingLabel: UILabel!
}
 
class PlaythroughCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBOutlet weak var ratingLabel: UILabel!
}

It doesn’t work!

Here’s the problem though: The field disappears in my storyboard!

I spent some time debugging this issue, and found that I could work around it by overriding the bgImage variable in my subclasses, but it was not neccessary to re-implement anything. Success!

The fix

Just adding this line did the trick:

@IBInspectable override var bgImage: UIImage! { didSet {} }

So now my complete file looks like this:

import Foundation
import UIKit
 
class MediaCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    var imgView = UIImageView()
    @IBInspectable weak var bgImage: UIImage! {
        didSet {
            imgView.frame = self.bounds
            self.addSubview(imgView)
            imgView.image = bgImage
            imgView.contentMode  = UIViewContentMode.Center
            self.sendSubviewToBack(imgView)
        }
    }
}
 
class TrailerCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}
 
class ReviewCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var ratingLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}
 
class PlaythroughCollectionViewCell: MediaCollectionViewCell {
    @IBOutlet weak var lengthLabel: UILabel!
    @IBOutlet weak var ratingLabel: UILabel!
    @IBInspectable override var bgImage: UIImage! { didSet {} }
}


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

Subscribe via RSS

Being Swifty with Collection View and Table View Cells

Here’s a common scenario: You have a table view or collection view that has a variety of different types of content. You want to display varying cells based on these types of content, and they’re all mixed within a single section. Pardon the stand-in art, but it looks roughly like this:

In the Objective-C world, it was typical to just use an NSArray to hold whatever records your collection view was going to be using as a data source, and then for each element check what class it is before picking a cell. This case seems particularly not Swifty these days.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"identifier" forIndexPath:indexPath];
    id record = self.records[indexPath.row];
    if([record isKindOfClass:[PlaythroughItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[ReviewItem class]]) {
        // ...
    }
    else if([record isKindOfClass:[TrailerItem class]]) {
        // ...
    }
    return cell;
}

shudder

Okay so that’s not the most type-safe approach, although it’s surprisingly common in Obj-C iOS code. As a better alternative in Swift, we can use enum cases for the different item types, and perform lookups of the items themeselves as needed. Let’s look at an example.

Example

In an entertainment app I’m working on I’ve got a few types of cells for the types of news items that come in:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

The index is just a way to keep track of which item this it supposed to represent in the database. We take this approach to keep the amount of data needed to produce the collection view down. We don’t need all the associated data with every video to be present when putting together a collection view, we just need the info on what cell is tapped, after it is tapped.

Let’s say we have a simple collection view that shows one of these three and picks a custom cell for each. In a Swift file NewsFeed.swift I have that acts as the dataSource of my collection view for the main news view. Of particular interest is the cellForItemAtIndexPath method, which runs the NewsItem record through a switch and produces the correct type of cell, with the relevant information populated:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let record = records[indexPath.row]
    switch(record) {
    case .Playthrough(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        let playthrough = MediaDB.playthroughAtIndex(index)
        cell.titleLabel.text = playthrough.title
        cell.lengthLabel.text = playthrough.length.prettyTime
        return cell
    case .Review(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        let review = MediaDB.reviewAtIndex(index)
        cell.ratingLabel.text = "\(review.rating) out of 10"
        cell.titleLabel.text = review.title
        return cell
    case .Trailer(let index):
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        let trailer = MediaDB.trailerAtIndex(index)
        cell.titleLabel.text = trailer.title
        cell.lengthLabel.text = trailer.length.prettyTime
        return cell
    }
}

This code works well enough, record is of type NewsItem which can be one of three cases for the different news items we support:

enum NewsItem {
  case Trailer(index: Int)
  case Review(index: Int)
  case Playthrough(index: Int)
}

The associated index value is so that we can find the individual item in the DB when the collection view wants to display a cell.

Something about this code didn’t sit right with me though. I felt that too much of the code was boilerplate; in particular the switch felt bulky and like it had too much work being done inside each case.

But what if I created a protocol for any data source that could be presented as a collection view cell? It would change on a view-by-view basis so I don’t actually want this in my model.. but I do like having it on these particular CollectionViewCell subclasses.

So, I created a protocol called NewsCellPresentable, which I can adhere to in extensions with my custom collection view cells:

protocol NewsCellPresentable {
    func configureForIndex(index: Int)
}
extension PlaythroughCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let playthrough = MediaDB.playthroughAtIndex(index)
        self.titleLabel.text = playthrough.title
        self.lengthLabel.text = playthrough.length.prettyTime
    }
}
extension ReviewCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let review = MediaDB.reviewAtIndex(index)
        self.titleLabel.text = review.title
        self.ratingLabel.text = "\(review.rating) out of 10"
    }
}
extension TrailerCollectionViewCell: NewsCellPresentable {
    func configureForIndex(index: Int) {
        let trailer = MediaDB.trailerAtIndex(index)
        self.titleLabel.text = trailer.title
        self.lengthLabel.text = trailer.length.prettyTime
    }
}

This feels much cleaner already. Now I can go back to my cellForItemAtIndexPath method and trim it down to just the following:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let record = records[indexPath.row]
    var cell: NewsCellPresentable
    switch(record) {
    case .Playthrough(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("PlaythroughCell", forIndexPath: indexPath) as! PlaythroughCollectionViewCell
        cell.configureForIndex(index)
    case .Review(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("ReviewCell", forIndexPath: indexPath) as! ReviewCollectionViewCell
        cell.configureForIndex(index)
    case .Trailer(let index):
        cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrailerCell", forIndexPath: indexPath) as! TrailerCollectionViewCell
        cell.configureForIndex(index)
    }
    return (cell as! MediaCollectionViewCell)
}

What do you think? Is this a cleaner approach? Let me know if you have a different method in the comment, or let me know on Twitter. My username is @jquave.

P.S.

If you want to try this out yourself and don’t have the same DB layer as me… guess what? Neither do I! You can easily stub this out like this:

class MediaDB {
    class func titleForRecord(index: Int) -> String {
        return "Title!!"
    }
    class func trailerAtIndex(index: Int) -> Trailer {
        return Trailer()
    }
    class func reviewAtIndex(index: Int) -> Review {
        return Review()
    }
    class func playthroughAtIndex(index: Int) -> Playthrough {
        return Playthrough()
    }
}
struct Trailer {
    let title = "Trailer Title"
    let length = 190
}
struct Review {
    let title = "Review Title"
    let rating = 4
}
struct Playthrough {
    let title = "Playthrough Title"
    let length = 9365
}
enum NewsItem {
    case Trailer(index: Int)
    case Review(index: Int)
    case Playthrough(index: Int)
}

Personally I always stub things out with static values before I do the work of integrating with a backend service or whatever data provider is needed. This makes it much easier to iterate.


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

Subscribe via RSS

Developing tvOS Apps for Apple TV [Part 2]

Looking for some help building your Apple TV tvOS App? I’m available for consulting and development, contact me.

This is part 2 of the tvOS tutorial. If you haven’t gone through part 1 yet, I suggest you run through that first.

Adding Interactivity

In the first section we created a simple TVML document with a few buttons. It looked something like this:

<document>
    <alertTemplate>
        <title>Hello tvOS!</title>
        <button>
            <text>A Button</text>
        </button>
        <button>
            <text>A Second Button</text>
        </button>
    </alertTemplate>
</document>

This is an alert with a few buttons, and they don’t do anything. It’s also rather specific and hard-coded, so maybe what would be better is to generate the XML on the fly. We can do this fairly easily in JS. Let’s add a new function to our main.js file that will encapsulate this alert in to a simpler alert with just an OK button.

function alert(str) {
  var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <alertTemplate>
      <title>Hey Listen!</title>
      <description>${str}</description>
      <button>
        <text>OK</text>
      </button>
    </alertTemplate>
  </document>`
  var parser = new DOMParser();
  var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
  navigationDocument.presentModal(alertDOMElement);
}

What we’re doing here is creating a string called alertXMLString that contains the TVML contents of a simple alert template with just 1 button. However for the description we’re using the TVJS string concatenation ${variable} to inject the value of str.

Next, we create a new DOMParser object that converts this string in to an actual XML DOM element.

Finally, we can present the DOM element modally using the presentModal method of the navigationDocument, a globally defined variable that will always contain the root navigation document.

Now, in our onLaunch function, let’s remove the code we had from before and simply call an alert…

App.onLaunch = function(options) {
    alert("Hello!");
}

tvOS Alert View

Run the app and you’ll see a cool tvOS alert saying “Hello!” However, clicking OK does nothing. So how do we handle event’s such as touch?

Basically, if you want to stay in JavaScript and TVML, you will want to add an event listener to the DOM element. So for example, we could add a second argument to the alert function, so that we can pass in a function that is called when the OK button sends the event for being “selected”. We’ll add this argument and call it doneCallback. To add the event listener, we can simply do so like this:

alertDOMElement.addEventListener("select", function() { doneCallback }, false);

So after updating the entire function, it should look like this:

function alert(str, doneCallback) {
  var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <alertTemplate>
      <title>Hey Listen!</title>
      <description>${str}</description>
      <button>
        <text>OK</text>
      </button>
    </alertTemplate>
  </document>`
  var parser = new DOMParser();
  var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
  alertDOMElement.addEventListener("select", doneCallback, false);
  navigationDocument.presentModal(alertDOMElement);
}

Now, we can modify our original onLaunch function to add a callback function that will present a TVML screen. Before we do that, let’s also add a function called getDocumentContents which similarly implements a callback function when loading is done, which passes in the XMLHttpRequest object’s response to the callbacks only argument. This allows us to more easily load in various types of TVML files.

function getDocumentContents(url, loadCallback) {
  var templateXHR = new XMLHttpRequest();
  templateXHR.responseType = "document";
  templateXHR.addEventListener("load", function() { loadCallback(templateXHR) }, false);
  templateXHR.open("GET", url, true);
  templateXHR.send();
  return templateXHR;
}

This is nearly the same code as the getDocument method we defined earlier, except this is asynchronous and does not actually push anything on to the view.

Now, with this function we can perform this call and then do a screen replace of the alert when the OK button is clicked.

App.onLaunch = function(options) {
    alert("Hello!", function() {
      var helloDocument = getDocumentContents("http://localhost:8000/hello.tvml", function(xhr) {
        navigationDocument.dismissModal();
        navigationDocument.pushDocument(xhr.responseXML);
      });
    });
}

Let’s also change our hello.tvml file to use the stackTemplate, since it’s more interesting to look at. A stackTemplate is a nice way to lay out a list of content with title’s and images. Here’s the example I’ll be using in my demo:

<document>
    <stackTemplate>
        <banner>
            <title>Which Artist Do You Prefer?</title>
        </banner>
        <collectionList>
            <shelf>
                <section>
                    <lockup>
                        <img src="http://localhost:8000/nina.png" width="256" height="256" />
                        <title>Nina Simone</title>
                    </lockup>
                    <lockup>
                        <img src="http://localhost:8000/coltrane.png" width="256" height="256" />
                        <title>John Coltrane</title>
                    </lockup>
                </section>
            </shelf>
        </collectionList>
    </stackTemplate>
</document>

So basically this is just the way a stackTemplate is laid out, the banner is the top banner, the collectionList is just something that contains many shelf objects, which is just something that contains section objects, which contains lockup objects. The lockup object is what actually contains our image and title. In my case I added some images to work with to my directory called nina.png and coltrane.png

tvOS stackTemplate Screenshot


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

Subscribe via RSS

Developing tvOS Apps for Apple TV [Part 1]

tvOS - Apple TV SDK Released

Here’s what you’ll have at the end of part 2 of this tutorial if you follow along:

Looking for some help building your Apple TV tvOS App? I’m available for consulting and development, contact me.

Apple TV Developer Tools

Before we get started you’ll need the Xcode 7.1 beta, which you can download here:
Download Xcode 7.1 Beta
Note that this does require an Apple Developer account, and is currently pre-release, so some things may change!

One thing to note when installing is that there is a known issue if you rename the Xcode 7.1 app, apparently people do this. So just a forewarning… don’t do that or you’ll get a crashy tvOS Simulator.

Also note that while Yosemite is supported, the capabilities are limited. It is recommend that for tvOS development you use OS X 10.11 El Capitan or newer. The El Capitan beta can be downloaded here.

Let’s start with some simple definitions…

Becoming an Apple TV Developer – Vocabulary

TVMLKit

TVMLKit is Apple’s new framework designed for working with Javascript and XML to provide a rich user-interface on tvOS, while allowing your apps logic to be written in Swift or Objective-C.

TVML

TVML is short for “TV Markup Language” and is basically just some XML that describes how items are laid out in a client-server based tvOS app. When laying out an interface, we want to use some TVML templates that Apple provides to create our UI, and then we can script the interactions in TVJS.

TVJS

As far as I can tell, TVJS is just JavaScript like you might already be familiar with.

Hello World

Let’s start out by making a basic hello world. In terms of Apple TV, we could just log “Hello World” to the console and that would be a decent start, but let’s start digging in to some of the Apple TV’s TVMLKit elements and put up a template on-screen.

First, open Xcode 7.1 and create a new project. You’ll see a list of templates appear. On the left-hand side we want to select **CHANGE tvOS, and then select Single View Application* template.

This will create a number of files from the tvOS template which are going to be useful in setting up our UI, and gives us a simple entry point to start coding Swift as well.

Setting up the main TVJS file

So basically the “server” in client-server tvOS apps is the TVML and JavaScript files, and whatever associated data they need to operate. The JavaScript file will be what loads in the TVML, and adds the page to the view stack. Another way to think of this is that the JavaScript file acts as a router or controller for the TVML files which are basically the views.

Kicking things off

First, we want to make some modifications to the AppDelegate.swift file that launches the app. For one, we need our application to adhere to the TVApplicationControllerDelegate protocol. This is defined in the TVMLKit framework, so we’ll need to import that as well. So you’ll want to update your AppDelegate.swift to include those two items:

import TVMLKit
 
class AppDelegate: UIResponder,
                   UIApplicationDelegate,
                   TVApplicationControllerDelegate {
 
....

This interface has four functions that tvOS calls upon the implementing AppDelegate to notify our app of the lifecycle of the tvOS application. We don’t need to worry about these just yet, but we’ll dig in to these in later tutorials. For now, just add the protocol as shown in the code snippet above, and that’s enough to get us started.

Next, we need to add some code to actually launch our JS file. This is a beta, and actually requires we do this work ourselves, I’m sure in future Xcode version’s this will simply be a template.

In the application didFinishLaunchingWithOptions function we need to perform a few steps. These steps are unlikely to change for each individual app so you can simply copy this snippet here:

// We'll hold a reference to the app controller in an optional property
var appController: TVApplicationController?
 
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  self.window = UIWindow(frame:UIScreen.mainScreen().bounds)
 
  let appControllerContext = TVApplicationControllerContext()
 
  let jsFilePath = NSURL(string: "http://localhost:8000/main.js")
  let javascriptURL = jsFilePath!
 
  appControllerContext.javaScriptApplicationURL = javascriptURL
  if let options = launchOptions
  {
    for (kind, value) in options
    {
      if let kindStr = kind as? String
      {
        appControllerContext.launchOptions[kindStr] = value
      }
    }
  }
 
  self.appController = TVApplicationController(context: appControllerContext, window: self.window, delegate: self)
 
  return true
}

In short, what this code does is it gets a reference to an TVApplicationControllerContext, a class that simply provides some launch data to our AppDelegate class, and gives us an interface to make adjustments to the launch process. Then it passes the URL of the main.js file we’re going to execute, and sets the path for the app controller to be that path.

We need to now add our javascript file, so click File > New, and then under the iOS tab you can select an Other > Empty file. Name this file main.js.

Do the same thing for a new file called hello.tvml.

In your main.js file we’ll add some relatively simple JavaScript to load in the hello.tvml file:

function getDocument(url) {
  var templateXHR = new XMLHttpRequest();
  templateXHR.responseType = "document";
  templateXHR.addEventListener("load", function() {pushDoc(templateXHR.responseXML);}, false);
  templateXHR.open("GET", url, true);
  templateXHR.send();
  return templateXHR;
}
 
function pushDoc(document) {
  navigationDocument.pushDocument(document);
}
 
App.onLaunch = function(options) {
  var templateURL = 'http://localhost:8000/hello.tvml';
  getDocument(templateURL);
}
 
App.onExit = function() {
  console.log('App finished');
}

Now in the hello.tvml file:

<document>
  <alertTemplate>
      <title>Hello tvOS!</title>
  </alertTemplate>
</document>

This TVML file is the meat of the UI. Documents must use templates or you’ll get crashes at runtime with the code we’re using. It just contains a simple template and a single title element.

One issue I found with setting things up is that I was unable to locally refer to these files, and instead they have to be on a web server. So, what is easiest to do is navigate to the location of your tvml and js files we just created and type the following command in the command line:

Setting up the server to host the files

python -m SimpleHTTPServer 8000

This just uses Mac OS’s built-in python interpreter to start a web server on port 8000 serving up the local directory. If you copied my code shown above you should now be able to boot in to your tvOS simulator by pressing the play button in Xcode! There is one more issue you should be aware of, which is that this is an http unsecured request. In iOS 9 this is blocked by default by App Transport Security. In order to allow localhost to be used in this way, we need to add a key to the Info.plist file.

Allowing Arbitrary Loads

Select your Info.plist file and add a new record by pressing one of the (+) buttons. In the list find “App Transport Security Settings” and press return. This will create a new Dictionary row, expand this and press the (+) button on this row to add a child row. For this one find, “Allows Arbitrary Loads” and set it to true. With this in place we can run the app and preview using the simulator.

Adding Buttons

What you’re seeing in this example is actually a template from Apple called the alertTemplate. You can also embed a few basic controls, such as text and buttons within the template. Try adding a few buttons to select from:

<document>
    <alertTemplate>
        <title>Hello tvOS!</title>
        <button>
            <text>A Button</text>
        </button>
        <button>
            <text>A Second Button</text>
        </button>
    </alertTemplate>
</document>

Here we just are adding child button elements, each with it’s own child text element. This in turn displays the fullscreen alert and the two buttons on the tvOS simulator. The official Apple documentation has a list of each template, and control you can use if you want to hit the ground running. Otherwise stay tight, subscribe, and I’ll be bringing you a full tutorial of a fully developed app very soon.

Next Steps

Proceed to PART 2 » of this tutorial to learn how to add interactivity to the app.

If you want me to email you letting you know when new posts are up, be sure to subscribe to my newsletter.

These are still early days, so expect issues. If you get stuck don’t hesitate to contact me on twitter @jquave

GO TO PART 2 »

Looking for an Apple TV Developer for your tvOS App? I’m available for consulting and development, contact me for more info.


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

Subscribe via RSS