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

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

List Comprehensions and Performance With Swift

This post written on August 15, 2015 to be compatible with Xcode 6 and Swift 1.2

List comprehensions provide a concise way to create lists. You can accomplish list comprehension-like operations in Swift even though list comprehensions are not mentioned in the Swift language guide.

Say you want to create a list of squares, like:

var squares = [Int]()
for x in 1..<10 {
    squares.append(x*x)
}

In Python you could use a list comprehension instead

squares = [x**2 for x in range(10)]

In Swift you can do

let squares = Array(map(1..<10) { $0 * $0 })

To get the sum of all the elements in the list you could do this

var sum = 0
for square in squares {
    sum = sum + square
}

Or use the reduce function

let sum = squares.reduce(0, { $0 + $1 })

Like list comprehensions in some other languages, you can use any Sequence or Collection as the input, not just a Range.

You can use map/reduce/filter/stride depending on what kind of list you are trying to create.

The two main perks of list comprehensions are conciseness and being able to generate faster bitcode.

My imitation list comprehension was more concise. I was curious whether it would also generate faster bitcode.

This article showed me how to analyze Swift assembly code using Hopper, an OSX and Linux disassembler. You can try Hopper for free without buying a license.

The code snippets without list comprehensions and with the imitation list comprehensions generated the same assembly code.

The assembly code from Hopper

Since both snippets created the same assembly code I assumed their execution time would be the same. We can demontrate this by measuring the execution of our program using XCTest.

My test for the code snippet with no list comprehension

func testNoListComprehensionPerformance() {
    self.measureBlock() {
        var squares = [Int]()
        for x in 1...5 {
            squares.append(x)
        }
    }
}

The relevant output

Test Case '-[speedTestTests.speedTestTests testNoListComprehensionPerformance]' started.

:0: Test Case '-[speedTestTests.speedTestTests testNoListComprehensionPerformance]' measured [Time, seconds] average: 0.000, relative standard deviation: 236.965%, values: [0.000154, 0.000005, 0.000004, 0.000004, 0.000004, 0.000004, 0.000004, 0.000004, 0.000004, 0.000004], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[speedTestTests.speedTestTests testNoListComprehensionPerformance]' passed (0.262 seconds).

My test for the code snippet with the imitation list comprehension

Test Case '-[speedTestTests.speedTestTests testSortaListComprehensionPerformance]' started.

:0: Test Case '-[speedTestTests.speedTestTests testSortaListComprehensionPerformance]' measured [Time, seconds] average: 0.000, relative standard deviation: 160.077%, values: [0.000045, 0.000005, 0.000004, 0.000003, 0.000003, 0.000003, 0.000003, 0.000004, 0.000003, 0.000003], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[speedTestTests.speedTestTests testSortaListComprehensionPerformance]' passed (0.255 seconds).

On average they only differ by only 0.007 seconds.

The coolest application of list comprehensions I've seen is in a spelling corrector. Airspeed Velocity wrote a Swift version of Peter Norvig's Python spelling corrector.

Conciseness is the main benefit of using list comprehension-like operations in Swift. Paul Graham wrote a great essay about how conciseness in programming languages is power. Since each programmer can only write a certain number of lines of code per day, you can accomplish more each day if you can accomplish more in the same number of lines. This power also makes you rethink what programs are possible. In a more verbose language this spelling corrector example could have seemed like an overwhelming project. I love how how something as technically complex and mysterious as a spelling corrector can be expressed in so few lines of code in Swift.


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

Subscribe via RSS

Function Currying in Swift

Function Currying in Swift

The concept of currying is that instead of accepting multiple arguments, a function accepts only one, and returns a function which acepts the remaining arguments. The returned function will also accept only one argument, and returns another function. This process continues until all arguments are exhausted and we are left only with a single return value.

For example, usually we define a function that returns the sum of two integers as follows:

func add1(x: Int, y: Int) -> Int {
    return x + y
}
add1(1, 2) // Output: 3

We can always transform a function taking multiple arguments into a curried one, by separating the function into a series of function that each takes only one argument. The curried version of add1 is as follows:

func add2(x: Int) -> (Int -> Int) {
    return { y in return x + y }
}
add2(1)(2) // Output: 3

This function has this type specified: Int -> Int -> Int. This may seem a little strange to newcomers to functional programming. Which part is the argument, and which part is the return type?

Here, add2 is taking an Int, and returning a Function which takes another Int, which in turn returns a third Int. You could say it’s something like this: Int -> (Int -> Int), or we could use typealias to make (Int -> Int) more clear:

typealias IntTransformer = (Int -> Int)

Now, any time we see IntTransformer, it’s easier to comprehend that it’s a function that transforms an Int value. With that, we could redfine add2 like this:

func add2Aliased(x: Int) -> IntTransformer {
...

This probably looks a little more familiar, but under the hood this is exactly the same as the add2 function we defined with the data type Int -> Int -> Int. This is just a more familiar looking way to write it.

Calling add2() with just a single argument returns a function that takes another (Int -> Int) function, which means we can store that function in a separate variable if we so choose:

let addTwentyTransformer = add2(20)
addTwentyTransformer(5) // Output: 25

Now, add2(20) is a function that takes one integer and returns the value of that integer plus 20.

The -> operator is defined as associativity right. Instead of writing A -> (B -> (C -> D)), we can simply write A -> B -> C -> D.

Swift also supports another way to define a curried function:

func add3(x: Int)(y: Int) -> Int {
    return x + y
}
add3(1)(y: 2) // Output: 3

This is helpful if you want named parameters, which can sometimes help your code easier to read. It’s also easy to make the syntax the same as add2 and remove the explicit argument name.

func add4(x: Int)(_ y: Int) -> Int {
    return x + y
}
add4(1)(2) // Output: 3

Benefits of Currying

What are the benefits currying provides? Let’s look at the add functions above.

With add1 the regular function, we cannot apply this function until both of its arguments are ready. With the curried add2, we can apply one or two arguments.

Let’s see an example that uses add1 and add2 with mapto transform an array of integers by adding 7 to each.

// Apply map to the array using the two functions
let xs = [1, 2, 3]
let addSeven1 = { add1($0, 7) }
map(xs, addSeven1) // Output: [8, 9, 10]
let addSeven2 = add2(7)
map(xs, addSeven2) // Output: [8, 9, 10]

The curried function add2 is much cleaner in this case.

There is another case when curried functions have obvious advantages. To demonstrate the example, first we define a function (a custom operator) that composes two functions together:

// Define a new operator |> that has left associativity
infix operator |> { associativity left }
func |> <A, B, C>(f: B -> C, g: A -> B) -> A -> C {
    return { x in
        f(g(x))
    }
}

Let’s say we want to transform an array of integers by adding 7 to each element, and then adding 3 again. We could write:

let xs = [1, 2, 3]
 
// Returns a function that adds three to it's only argument
let addThree = add2(3)
 
// Apply addSeven1 to every item in xs
// Then apply addThree to every item in that list
xs.map(addSeven1).map(addThree) // Output: [11, 12, 13]

It first adds 7 to each element in xs, wraps the results into a temporary array, then add 3 to each in the temporary array, and return the last results in a new array. This creates a temporary array that we never need.

By composing the curried functions, addSeven2 and addThree, we can eliminate the creation of the temporary array.

xs.map(addSeven1 |> addThree) // Output: [11, 12, 13]

Builtin Currying Functions in Swift

In the Swift standard library there is a function on the Int type called advancedBy that takes an amount, and returns an Int that has been adjusted by that amount.

extension Int : RandomAccessIndexType {
    func advancedBy(amount: Distance) -> Int
}

It’s simple enough to use this function on an Int and get the advanced value:

5.advancedBy(6) // Output: 11

But because of function currying, we could get the partial application of this function by not specifying the initial value to be advanced:

let add6 = Int.advancedBy(6)
add6(5) // Output: 11
add6(10) // Output: 10

Let’s look at the following example. To insert another string at the end of the given string, we could call the splice function on an instance of String:

var s = "Hello"
s.splice(", world", atIndex: s.endIndex)
// Output: "Hello, world"

Or we can call it on String data type, and pass the String instance as its only argument:

String.splice(&s)("!!!", atIndex: s.endIndex)
s // Output: "Hello, world!!!"

The splice function on a String instance is not a curried function. s.splice("!!!")(atIndex: s.endIndex) will not compile.

The term partial application, is a function that accepts only some of its arguments, and returns another function that takes the rest of arguments. While a curried function takes only one argument.

Don’t confuse partial application with another term called partial function. Partial function is a function that cannot provide a valid result for all its possible inputs. In Objective-C, if we call [[NSString alloc] initWithString:nil], it will compile but throw a NSInvalidArgumentException at runtime. -initWithString: is a partial function, because there is no return value for nil.

Next Steps

Is this all making sense? This can all be quite a chunk of new information to take in if you are new to functional programming in general. For that reason we are preparing a free functional programming course that is in private beta testing right now. If you want to be part of the beta, or just want us to let you know when it’s ready, sign up for the beta here.


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

Subscribe via RSS

Functional Programming in Swift

Thoughts on Functional Programming in Swift

Like most of you, I have to use Objective-C at my day job. I could only craft my Swift skills at night. Swift is not a purely functional language. It can be use imperatively because all frameworks from Apple are written in Objective-C at the time of writing. However, it is also functional, learning from modern languages like Haskell, F#, etc. At the beginning when reading Swift blogs and tutorials, many people talked about terms like functors and monads, those sounded alien-like to me. I started to learn Haskell to understand what they were talking about. I’m not an expert, and still learning functional programming and Swift, but I wanted to share what I’ve learned so far. Hopefully it will save you some troubles to get into the functional programming world.

Key Concepts

Higher-order functions

One key concept of functional programming is higher-order functions. According to Wikipedia, a higher-order function:

  • takes one or more functions as an input
  • outputs a function

In Swift, functions are first-class citizens, like structs and classes, as we can see in the following example:

let addFive = { $0 + 5 }
addFive(7) // Output: 12

We define a function as an inline closure, and then assign it to an inline closure. Swift provides shorthand argument to it. Instead of by name, we can refer to the arguments by number, $0, $1 and so on.

func addThreeAfter(f: Int -> Int) -> Int -> Int {
    return { f($0) + 3 }
}
let addEight = addThreeAfter(addFive)
addEight(7) // Output: 15

The argument type Int -> Int means it is a function that takes an Int as an argument, and returns an Int. If a function requires more than one arguments, for example, it has two Int argument, we can define the type as (Int, Int) -> Int.

The return type of addThreeAfter is also a function. It equivalents to func addThreeAfter(f: Int -> Int) -> (Int -> Int).

[addFive, addEight].map { $0(7) } // Output: [12 15]

map is a special function for container type, such as Array, Optional. It unwraps values from the container type, then applies a transform for each, and wraps them again. We use map here to pass 7 as an argument to each function in the array, and wraps the results into a new array.

In summary, we can assign functions to variables, store them in data structures, pass them as arguments to other functions, and even return them as the values from other functions.

Pure functions

A function is called a pure function if it has no observable side effects. But what the heck is side effect?

A function or expression is said to have a side effect if, in addition to returning a value, it also modifies some state or has an observable interaction with calling functions or the outside world.
— from Wikipedia

The two functions addFive and addEight are pure functions. They don’t modify the input value, or change any global state. In the example below, addFive2 modifies the input, so it is not a pure function.

func addFive2(inout a: Int) -> Int {
    a += 5
    return a
}
var a = 7
addFive2(&a)
a // Output: 12

Functions that access or modify a database, the local file system, or printing strings to the screen are also considered impure. Because they modify the state of the database records, file system, and display buffers respectively. Sometimes side effects are desirable. Without side effects, we could not interact with the program.

Haskell introduces types like IO to separate pure and impure layers. But in Swift, we don’t need to worry too much about this separation. We could still use Cocoa APIs as usual. But, I strongly encourage the use of pure functions whenever possible. With pure functions, the code is more predictable. A pure function will always return the same value if given the same input. It’s easy to test, too. We don’t need to mock a data structure to satisfy its internal state requirements to test a function.

Imperative & Functional Programming

All above are very theoretical. You may want to know how functional programming with help us solve problems in a better, clearer, or less error-prone way. What are the benefits we could gain from it?

First, you need to understand that we could do almost anything in imperative programming, and functional programming does not extend the possibilities of what we could do.

Second, if you come from the imperative programming world, some functional code is harder to understand at first, especially those with custom operators. It is not because functional programming is hard and obscure, but it’s because our mindsets are trained from years of imperative programming practices.

Take a look at this example of imperative code, which we can rewrite as functional code. These two do exactly the same things.

// Imperative
var source = [1, 3, 5, 7, 9]
var result = [Int]()
for i in source {
    let timesTwo = i * 2
    if timesTwo > 5 && timesTwo < 15 {
        result.append(timesTwo)
    }
}
result // Output: [6, 10, 14]
// Functional
let result2 = source.map { $0 * 2 }
                    .filter { $0 > 5 && $0 < 15 }
result2 // Output: [6, 10, 14]

It is arguable which one is clearer. But from this simple example you can see the main difference between imperative and functional programming. In imperative programming, we instruct the computer how to do something:

  1. Iterate through source
  2. Get the result from the element, and multiply by 2
  3. Compare it with 5 and 15
  4. If it is bigger than 5 and less than 15, put it into result

However, in functional programming, we describe what to do:

  1. Transform each element in source to itself multiplied by 2
  2. Only select the ones with value bigger than 5 and less than 15

I’m not going to persuade you functional programming is better. It’s your personal preference. After all, good code is all about writing code that:

  1. Works as intended
  2. Is clear to you and your team

An Example: Reverse Polish Notation Calculator

I like Swift and functional programming, because it enables me to solve a problem in a different perspective. There is usually more than one way to solve a problem. Exploring a better solution helps us grow to become good developers.

Let me show you a functional example. It is a calculator for algebraic expressions of reverse polish notation, or RPN in short. (It is a Swift implementation of the Haskell example in Learn You a Haskell for Great Good.)

A RPN expression of (3 + 5) 2 is 3 5 + 2 . You may think of this as a stack of numbers. We go through the RPN expression from left to right. When encountering a number, we push it onto the stack. When we encounter an operator, we pop two numbers from the stack, use the operator with those two numbers, and then push the result back onto the stack. When reaching the end of the expression, the only one number left on the stack is the result (assuming the RPN expression is valid). For more explanation about RPN, please check on Wikipedia.

We want a function that returns the result for an RPN expression.

func solveRPN(expression: String) -> Double {
    // Process the expression and return the result
}

Given a RPN expression String “3 5 + 2 *”, first we need to transform it into an array of elements that we can process. There are two kind of elements, operand and operator. The Enum data type in Swift comes in handy for defining the element. We name it RPNElement.

enum RPNElement {
    case Operand(Double)
    case Operator(String, (Double, Double) -> Double)
}

Next, we split the expression into an array of Strings, then map it into an RPNElement array.

extension String {
    func toRPNElement() -> RPNElement {
        switch self {
        case "*": return .Operator(self, { $0 * $1 })
        case "+": return .Operator(self, { $0 + $1 })
        case "-": return .Operator(self, { $0 - $1 })
        default: return .Operand(Double(self.toInt()!))
        }
    }
}
func stringToRPNElement(s: String) -> RPNElement {
    return s.toRPNElement()
}
func solveRPN(expression: String) -> Double {
    let elements = expression.componentsSeparatedByString(" ").map(stringToRPNElement)
        // Further process
}

Next, we will go through the array and process it according to how RPN works, as I described earlier. We reduce the array into a Double array. Assuming the expression is valid, the Double array should only contain one element. It will be the result we want.

func solveRPN(expression: String) -> Double {
    let elements = expression.componentsSeparatedByString(" ").map(stringToRPNElement)
    let results = elements.reduce([]) { (acc: [Double], e: RPNElement) -> [Doublein
        switch e {
        case .Operand(let operand):
            return [operand] + acc
        case .Operator(let op, let f):
            let r = f(acc[0], acc[1])
            return [r] + acc[2..<acc.count]
        }
    }
    return results.first ?? 0
}
solveRPN("3 5 + 2 *") // Output: 16

Where to Go From Here?

If you are interested in learning more about functional programming, I highly recommend the following two books:

Even though the second book is written for Haskell, but the concepts also apply to Optional in Swift as well. Besides, it explains Functor, Applicative, Monad in details.


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

Subscribe via RSS

Local Notifications in iOS 10 with Swift (Part 1)

This post has been updated for compatibility with XCode 8 and iOS 10

Local notifications are a simple way to display information to your user even when your app is in the background. They allow you to display an alert, play a sound or badge your app’s icon. Local notifications can be triggered at a scheduled time or when your user enters or leaves a geographical area. In this tutorial, we’ll create a simple to-do list application and delve into a number of UILocalNotification features and quirks.

First, let’s create a new single view application in Xcode and call it LocalNotificationsTutorial. Remember to choose Swift as the language.

Screen Shot 2015-01-30 at 10.15.42 PM

Before we delve into writing any code, let’s get our view controllers and views set up. This is a necessary step, and I’ll be covering some of the basics of using Interface Builder, but if want to skip it and jump right into dealing with scheduling notifications you can follow along by getting the configured application from here.

Configuring the Views

Our finished application will have two views in a navigation controller: a root view that displays a chronologically ordered list of to-do items with deadlines, and a view for creating to-do list items.

iOS Simulator Screen Shot Feb 1, 2015, 11.43.36 PM iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM

Creating the View Controllers

Before opening Interface Builder, we should generate view controllers to back each of our views. Ctrl or right-click on the project group in the project navigator and select “New File”.

Screen Shot 2015-01-30 at 10.29.29 PM

Select “Cocoa Touch Class” from the “iOS -> Source” menu and create a subclass of UITableViewController named “TodoTableViewController”. Don’t create a XIB file, and, of course, the language should be Swift. This will be our root view controller and it will be in charge of displaying the to-do list.

We need a separate view for creating our to-do items. Repeat the process, this time create a UIViewController subclass and name it “TodoSchedulingViewController”.

Setting up Navigation

Now that our view controllers are created, let’s hook them into our project’s storyboard. Click “Main.storyboard” and delete the root view controller. Go ahead and delete “ViewController.swift” as well. We won’t be using it.

Drag a new navigation controller from the object library onto the now blank storyboard. We’ve deleted our root view, so drag a Storyboard Entry Point onto the navigation controller so our application will have a root view.

Screen Shot 2015-01-30 at 11.11.26 PM

The object library

Select the navigation controller’s root view (a table view) and set its custom class to “TodoTableViewController” in the identity inspector.

Screen Shot 2015-01-30 at 11.22.17 PM

The identity inspector

Since we’re going to display deadlines for each to-do item, we need to select the table view’s first (and only) prototype cell, switch to the attributes inspector, and set the cell’s style to “Subtitle”.  It needs a reuse identifier as well, so we can refer to it in our code. We’ll use “todoCell”.

Screen Shot 2015-01-30 at 11.39.00 PM

The attributes inspector

Keep the attributes inspector selected. Drag a navigation item onto the table view and give it the title “Todo List”, then drag a bar button item onto that and set the identifier to “Add”.

Now we’ll set up the view on which users can schedule and title their to-do items. Drag a view controller into the storyboard. Its custom class should be set to “TodoSchedulingViewController”.

Ctrl or right-click on the “Add” button, drag from the “action” to the new view, and select “show”. Now all our navigation is linked up.

Screen Shot 2015-01-31 at 12.03.33 AM

We need to drag three controls onto this view: a text field (with “Title” as the placeholder text), a date picker and a button (titled “Save”). Just center and widen all three, then “add missing constraints” to all views in the view controller (Under “Resolve Auto Layout Issues”, the triangle icon towards the bottom right of Xcode). Adding constraints ensures that the view is laid out predictably across various screen sizes (instead of having portions of controls cut off or misaligned).

Screen Shot 2015-01-31 at 12.25.08 AM

Connecting Controls to Code

Now that our views and navigation are laid out, we have to link our text field and date picker controls to an IBOutlet in TodoSchedulingViewController.swift. This will allow us to access these controls (and their values) in our code. There are a few ways to do this, but the simplest is to enable the Assistant editor by clicking the interlocking circles in the top right of XCode, Ctrl or right-click the control, and drag the “New Referencing Outlet” circle into the TodoSchedulingViewController class body.

Screen Shot 2015-01-31 at 12.45.14 AM

Do this for both the text field and the date picker, naming them “titleField” and “deadlinePicker” respectively.

@IBOutlet weak var titleField: UITextField!
@IBOutlet weak var deadlinePicker: UIDatePicker!

The final step is to connect the save button to an IBAction (an event handler function). Just Ctrl or right-click the button, and drag from the “Touch Up Inside” circle to the code window. Name the action “savePressed” and optionally set the sender type to UIButton (no other controls will be firing this action, so we can be more specific).

@IBAction func savePressed(_ sender: UIButton) {
}

The views and navigation for this project are all set up. Go ahead and run the app in the simulator. Try it for a few different devices. You’ll notice that, because of the constraints we added, your views stretch or squeeze to fit the various screen sizes.

Now, let’s get out of Interface Builder and write some code.

Registering Notification Settings

We need to register our intent to use notifications with the application. Otherwise, the notifications we schedule simply won’t fire. Switch over to your Application Delegate (AppDelegate.swift) and add the following line to application:didFinishLaunchingWithOptions:

application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))

On the first launch of the app, users will now be prompted to allow your app to send notifications. If the user grants permission, we will be able to schedule notifications that display a banner, play a sound, and update our app icon’s badge number (which we’ll cover in part 2).

iOS Simulator Screen Shot Feb 3, 2015, 2.56.37 PM

Modeling the Application

For a simple app like this, it may be tempting to handle all of the logic in the view controllers that we just created, but we’ll have an easier time understanding and maintaining our code if we keep the management of the to-do list and the presentation logic separate.

I chose to model individual to-do items with a lightweight struct. Let’s create that now. Just click “File -> New -> File”, choose “Swift File” and name it “TodoItem”. Each to-do list item has a title and a deadline, so we’ll create properties for both.

struct TodoItem {
  var title: String
  var deadline: Date
}

Ultimately, each to-do list item needs to be backed by an on disk representation, so that the list will persist if the application is terminated. Instances of UILocalNotification have a userInfo property – a dictionary that we can use to store miscellaneous data like the title, but we can’t rely on that alone in this case. Local notifications are automatically unscheduled after they are fired, which means that we wouldn’t be able to retrieve past-due items. We’ll have to use another method to persist our items, and we need a way to associate an item we’ve retrieved from the disk with its corresponding local notification. For that, we’ll use a universally unique identifier (UUID).

struct TodoItem {
    var title: String
    var deadline: Date
    var UUID: String

    init(deadline: Date, title: String, UUID: String) {
        self.deadline = deadline
        self.title = title
        self.UUID = UUID
    }
}

Since we’re going to display overdue items in red, lets also add a convenience method that returns whether or not an item is overdue.

var isOverdue: Bool {
    // Optionally, you can omit "ComparisonResult" and it will be inferred.
    return (Date().compare(self.deadline) == ComparisonResult.orderedDescending) // deadline is earlier than current date
}

Saving To-Do Items (Scheduling Notifications)

We need a class to represent the list of items and handle persisting them. Create a new Swift file named “TodoList”.

Our application is only concerned with maintaining a single to-do list, so it makes sense to make a single shared instance available throughout the app.

class TodoList {
    class var sharedInstance : TodoList {
        struct Static {
            static let instance: TodoList = TodoList()
        }
        return Static.instance
    }
}

This method is the community-accepted way to implement the singleton pattern in Swift, which you can adapt to your own projects. If you’re curious, you can read the details about what it’s doing and why in this Stack Overflow answer.

UserDefaults provides a simple way to persist our to-do items to disk. The following snippet defines a method that adds a dictionary representation of a to-do item to standard user defaults (with UUID as the key), and then creates the associated local notification.

fileprivate let ITEMS_KEY = "todoItems"
func addItem(_ item: TodoItem) {
    // persist a representation of this todo item in UserDefaults
    var todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? Dictionary() // if todoItems hasn't been set in user defaults, initialize todoDictionary to an empty dictionary using nil-coalescing operator (??)
    todoDictionary[item.UUID] = ["deadline": item.deadline, "title": item.title, "UUID": item.UUID] // store NSData representation of todo item in dictionary with UUID as key
    UserDefaults.standard.set(todoDictionary, forKey: ITEMS_KEY) // save/overwrite todo item list
 
    // create a corresponding local notification
    let notification = UILocalNotification()
    notification.alertBody = "Todo Item \"\(item.title)\" Is Overdue" // text that will be displayed in the notification 
    notification.alertAction = "open" // text that is displayed after "slide to..." on the lock screen - defaults to "slide to view" 
    notification.fireDate = item.deadline as Date // todo item due date (when notification will be fired) notification.soundName = UILocalNotificationDefaultSoundName // play default sound 
    notification.userInfo = ["title": item.title, "UUID": item.UUID] // assign a unique identifier to the notification so that we can retrieve it later
 
    UIApplication.shared.scheduleLocalNotification(notification)
}

Notice that we’re just playing the default sound when the notification fires. You can provide your own sound file, but audio files over 30 seconds in length are not supported. The default sound will play instead.

We’re almost to the point where users can create new list items. It’s time to implement savePressed: in TodoSchedulingViewController.

@IBAction func savePressed(_ sender: UIButton) {
    let todoItem = TodoItem(deadline: deadlinePicker.date, title: titleField.text!, UUID: UUID().uuidString)
    TodoList.sharedInstance.addItem(todoItem) // schedule a local notification to persist this item
    let _ = self.navigationController?.popToRootViewController(animated: true) // return to list view
}

Note that, since this is a new to-do list entry, we’re passing in a newly generated UUID.

Try out the app now. Launch it in the simulator, create a new item due a minute in the future, and return to the home or lock screen (Shift-CMD-H or CMD-L) to view the notification. The notification won’t necessarily fire right on the stroke of the minute (due to a hidden ‘seconds’ value on the time picker control), but you’ll see it within the minute.

iOS Simulator Screen Shot Feb 3, 2015, 4.29.05 PMiOS Simulator Screen Shot Feb 3, 2015, 4.33.13 PM

The 64 Notification Limit

It’s important to note that you’re limited to scheduling 64 local notifications. If you schedule more, the system will keep the 64 soonest firing notifications and automatically discard the rest.

We can avoid running into this issue by disallowing the creation of new items if 64 already exist.

In TodoTableViewController:

func refreshList() {
    todoItems = TodoList.sharedInstance.allItems()
    if (todoItems.count >= 64) {
        self.navigationItem.rightBarButtonItem!.enabled = false // disable 'add' button
    }
    tableView.reloadData()
}

Retrieving To-Do Items

The fact that to-do items are persisted as an array of dictionaries is an implementation detail that outside classes shouldn’t have to worry about. Our TodoList class needs a public facing function that the list view controller can query to retrieve a list of to-do items.

func allItems() -> [TodoItem] {
    let todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? [:]
    let items = Array(todoDictionary.values)
    return items.map({
        let item = $0 as! [String:AnyObject]
        return TodoItem(deadline: item["deadline"] as! Date, title: item["title"] as! String, UUID: item["UUID"] as! String!)
    }).sorted(by: {(left: TodoItem, right:TodoItem) -> Bool in
        (left.deadline.compare(right.deadline) == .orderedAscending)
    })
}

This function retrieves the array of item representation from disk, converts it to an array of TodoItem instances using an unnamed closure we pass to map, and sorts that array chronologically. Describing the map and sort functions in detail is beyond the scope of this tutorial, but you can find more information in the Swift language guide’s section on closures.

Now we can hook up TodoTableViewController to display the list.

class TodoTableViewController: UITableViewController {
    var todoItems: [TodoItem] = []
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        refreshList()
    }

    func refreshList() {
        todoItems = TodoList.sharedInstance.allItems()

        if (todoItems.count >= 64) {
            self.navigationItem.rightBarButtonItem!.enabled = false // disable 'add' button
        }
        tableView.reloadData()
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "todoCell", for: indexPath) // retrieve the prototype cell (subtitle style)
        let todoItem = todoItems[(indexPath as NSIndexPath).row] as TodoItem
        cell.textLabel?.text = todoItem.title as String!
        if (todoItem.isOverdue) { // the current time is later than the to-do item's deadline
            cell.detailTextLabel?.textColor = UIColor.red
        } else {
            cell.detailTextLabel?.textColor = UIColor.black // we need to reset this because a cell with red subtitle may be returned by dequeueReusableCellWithIdentifier:indexPath:
        }

        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "'Due' MMM dd 'at' h:mm a" // example: "Due Jan 01 at 12:00 PM"
        cell.detailTextLabel?.text = dateFormatter.string(from: todoItem.deadline as Date)
        return cell
    }
}

Our to-do list now shows each item in chronological order, with the date label in red if the item is overdue.

iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM

There are two issues we need to deal with here. Users currently don’t receive any visual feedback that a notification has fired (and a to-do item is overdue) when the app is running in the foreground. Also, when the app resumes, the list won’t automatically be refreshed, meaning that missed deadlines may not appear in red. Lets solve both issues now.

In TodoTableViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(TodoTableViewController.refreshList), name: NSNotification.Name(rawValue: "TodoListShouldRefresh"), object: nil)
}

In AppDelegate:

func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
    NotificationCenter.default.post(name: Notification.Name(rawValue: "TodoListShouldRefresh"), object: self)
}
func applicationDidBecomeActive(_ application: UIApplication) {      
    NotificationCenter.default.post(name: Notification.Name(rawValue: "TodoListShouldRefresh"), object: self)
}

Please note that, despite the presence of the word “notification”, NotificationCenter is unrelated to UILocalNotification. NotificationCenter’s purpose is to provide a simple way to implement the observer pattern in your apps.

Here we register TodoTableViewController as an observer to the “TodoListShouldRefresh” notification. Whenever a notification with that name is posted, the reloadData method will be called.

I’ve omitted this step, but it is generally better to define notification names as static constants to avoid repeating yourself.

Completing To-Do Items (Canceling Notifications)

Our to-do list application isn’t very useful without a way to clear out completed items, and the simplest way to do that is to delete them. We need to add some functionality to TodoList.

func removeItem(_ item: TodoItem) {
    let scheduledNotifications: [UILocalNotification]? = UIApplication.shared.scheduledLocalNotifications
    guard scheduledNotifications != nil else {return} // Nothing to remove, so return
 
    for notification in scheduledNotifications! { // loop through notifications...    
        if (notification.userInfo!["UUID"] as! String == item.UUID) { // ...and cancel the notification that corresponds to this TodoItem instance (matched
            UIApplication.shared.cancelLocalNotification(notification) // there should be a maximum of one match on UUID
            break
        }
    }

    if var todoItems = UserDefaults.standard.dictionaryForKey(ITEMS_KEY) {
        todoItems.removeValue(forKey: item.UUID)
        UserDefaults.standard.set(todoItems, forKey: ITEMS_KEY) // save/overwrite todo item list
    }
}

Note that passing an existing notification to scheduleLocalNotification: will cause a duplicate to be created. If you want to give users the ability to edit existing local notifications, you’ll need to retrieve the old one and cancel it before scheduling the new one.

Now we just need to allow users to remove items by swiping the item’s cell and pressing “Complete”.

In TodoTableViewController:

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true // all cells are editable
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete { // the only editing style we'll support
        // delete the row from the data source
        let item = todoItems.remove(at: (indexPath as NSIndexPath).row) // remove TodoItem from notifications array, assign removed item to 'item'
        tableView.deleteRows(at: [indexPath], with: .fade) 
        TodoList.sharedInstance.removeItem(item) // delete backing property list entry and unschedule local notification (if it still exists) 
        self.navigationItem.rightBarButtonItem!.isEnabled = true // we definitely have under 64 notifications scheduled now, make sure 'add' button is enabled
    }
}

iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM

Conclusion

We now have a working to-do list application that lets our users schedule and cancel local notifications with sound and custom alert messages. The source code can be downloaded here.

In part 2 of this series, which builds on this project, we’ll add support for an application icon badge and learn about notification actions, a new feature that allows us to trigger code from a notification without ever opening the app.

Go to part 2 now »


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

Subscribe via RSS

Local Notifications in iOS 9+ with Swift (Part 2)

This post has been updated for compatibility with XCode 8 and iOS 10

In part 1 of this series, we created a simple to-do list application that used local notifications to alert users when to-do items were overdue. This time, we’re going to build on that the project by enabling application icon badges to display the number of overdue items and add support for notification actions, allowing our users to complete and edit to-do items without even opening the app.

You can download the source code for part 1 here.

Badging the App Icon

It bears mentioning that we can badge the app icon without using local notifications. The applicationWillResignActive: method in AppDelegate is a good place to do so, since it will be fired just before the user returns to the home screen where they can see the app icon.

func applicationWillResignActive(_ application: UIApplication) { // fired when user quits the application
    let todoItems: [TodoItem] = TodoList.sharedInstance.allItems() // retrieve list of all to-do items
    let overdueItems = todoItems.filter({ (todoItem) -> Bool in
        return todoItem.deadline.compare(Date()) != .orderedDescending
    })
    UIApplication.shared.applicationIconBadgeNumber = overdueItems.count // set our badge number to number of overdue items
}

iOS Simulator Screen Shot Feb 4, 2015, 10.31.14 PM iOS Simulator Screen Shot Feb 4, 2015, 11.51.25 PM

This is a good start, but we need the badge number to automatically update when to-do items become overdue. Unfortunately, we can’t instruct the app to simply increment the badge value when our notifications fire, but we can pre-compute and provide a value for the “applicationIconBadgeNumber” property of our local notifications. Lets provide a method in TodoList to set an associated badge number for each notification.

func setBadgeNumbers() {
    let scheduledNotifications: [UILocalNotification]? = UIApplication.shared.scheduledLocalNotifications // all scheduled notifications
    guard scheduledNotifications != nil else {return} // nothing to remove, so return
 
    let todoItems: [TodoItem] = self.allItems()
 
    // we can't modify scheduled notifications, so we'll loop through the scheduled notifications and
    // unschedule/reschedule items which need to be updated.
    var notifications: [UILocalNotification] = []

    for notification in scheduledNotifications! {
        print(UIApplication.shared.scheduledLocalNotifications!.count)
        let overdueItems = todoItems.filter({ (todoItem) -> Bool in // array of to-do items in which item deadline is on or before notification fire date
            return (todoItem.deadline.compare(notification.fireDate!) != .orderedDescending)
        })

        // set new badge number
        notification.applicationIconBadgeNumber = overdueItems.count 
        notifications.append(notification)
    }
 
    // don't modify a collection while you're iterating through it
    UIApplication.shared.cancelAllLocalNotifications() // cancel all notifications
 
    for note in notifications {
        UIApplication.shared.scheduleLocalNotification(note) // reschedule the new versions
    }
}

There’s no way to update a scheduled notification, but you can achieve the same effect by simply canceling the notification, making your changes and rescheduling it.

The applicationIconBadgeNumber property can accept values up to 2,147,483,647 (NSIntegerMax), though anything over five digits will be truncated in the icon badge. Setting it to zero or a negative number will result in no change.

Screen Shot 2015-02-04 at 11.36.36 PMScreen Shot 2015-02-04 at 11.38.14 PM

Now we just need to call this method when our to-do list changes. Add the following line to the bottom of addItem: and removeItem: in TodoList

self.setBadgeNumbers()

Now, when a notification fires, the badge number will be automatically updated.

Repeating Notifications

UILocalNotificaiton instances have a repeatInterval property that we can use to, unsurprisingly, repeat a notification at a regular interval. This is a good way to get around the 64 notification limit in some cases; a repeating notification is only counted against it once.

Unfortunately, we have to choose between using repeatInterval and applicationIconBadgeNumber in this app. Badge numbers are set on the application icon each time a notification is fired. Older notifications could end up “out of phase” with newer notifications and actually lower the badge number when repeated. We could get around this by scheduling two notifications for each to-do item, a repeating notification that displays the alert and plays the sound, and a non-repeating notification that updates the badge count, but this would cut the number of to-do items we could schedule in half.

The biggest limitation with repeating notifications is that the repeatInterval doesn’t accept a custom time interval. You have to provide an NSCalendarUnit value like “HourCalendarUnit” or “DayCalendarUnit”. If you want a notification to fire every 30 minutes, for example, you’ll have to schedule two notifications (offset by 30 minutes) and set them both to repeat hourly. If you want it to fire every 31 minutes, then you’re out of luck.

Performing Actions in the Background

iOS 8 introduced a really useful new feature, notification actions, which let our users trigger code execution without even having to open the app. Lets give our users the ability to complete and schedule reminders for to-do items directly from the notification banner.

iOS Simulator Screen Shot Feb 6, 2015, 12.43.34 AM iOS Simulator Screen Shot Feb 6, 2015, 12.43.37 AM

In AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    let completeAction = UIMutableUserNotificationAction()
    completeAction.identifier = "COMPLETE_TODO" // the unique identifier for this action
    completeAction.title = "Complete" // title for the action button
    completeAction.activationMode = .background // UIUserNotificationActivationMode.Background - don't bring app to foreground
    completeAction.isAuthenticationRequired = false // don't require unlocking before performing action
    completeAction.isDestructive = true // display action in red
 
    let remindAction = UIMutableUserNotificationAction()
    remindAction.identifier = "REMIND"
    remindAction.title = "Remind in 30 minutes"
    remindAction.activationMode = .background
    remindAction.isDestructive = false
 
    let todoCategory = UIMutableUserNotificationCategory() // notification categories allow us to create groups of actions that we can associate with a notification
    todoCategory.identifier = "TODO_CATEGORY"
    todoCategory.setActions([remindAction, completeAction], for: .default) // UIUserNotificationActionContext.Default (4 actions max)
    todoCategory.setActions([completeAction, remindAction], for: .minimal) // UIUserNotificationActionContext.Minimal - for when space is limited (2 actions max)

    // we're now providing a set containing our category as an argument
    application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: Set([todoCategory])))
    return true
}

Notice that we’re calling todoCategory.setActions() twice, once for each of the two available action contexts. If your users are displaying notifications from your app as banners, then the actions in the minimal context will be displayed. If notifications are displayed as alerts (the “default” context), up to four actions will be displayed.

iOS Simulator Screen Shot Feb 6, 2015, 12.41.20 AM iOS Simulator Screen Shot Feb 6, 2015, 12.06.33 AM

The order of the actions in the array we pass to setActions: is the order that the actions will be displayed in the UI, though, oddly, the items are ordered right-to-left in the minimal context.

Lets make sure to set this category for the notification we’re scheduling in TodoList’s addItem: method.

notification.category = "TODO_CATEGORY"

We already have a method for “completing” to-do items, removeItem:, but we need to implement one for scheduling a reminder in TodoList.

func scheduleReminder(forItem item: TodoItem) {
    let notification = UILocalNotification() // create a new reminder notification
    notification.alertBody = "Reminder: Todo Item \"\(item.title)\" Is Overdue" // text that will be displayed in the notification
    notification.alertAction = "open" // text that is displayed after "slide to..." on the lock screen - defaults to "slide to view"
    notification.fireDate = Date(timeIntervalSinceNow: 30 * 60) // 30 minutes from current time
    notification.soundName = UILocalNotificationDefaultSoundName // play default sound
    notification.userInfo = ["title": item.title, "UUID": item.UUID] // assign a unique identifier to the notification that we can use to retrieve it later
    notification.category = "TODO_CATEGORY"
 
    UIApplication.shared.scheduleLocalNotification(notification)
}

Note that we aren’t changing the due date on the to-do item (or trying to cancel the original notification – it’s been automatically removed). Now we just have to jump back to AppDelegate and implement a handler for the actions:

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
    let item = TodoItem(deadline: notification.fireDate!, title: notification.userInfo!["title"] as! String, UUID: notification.userInfo!["UUID"] as! String!)
    switch (identifier!) {
    case "COMPLETE_TODO":
        TodoList.sharedInstance.remove(item)
    case "REMIND":
        TodoList.sharedInstance.scheduleReminder(forItem: item)
    default: // switch statements must be exhaustive - this condition should never be met
        print("Error: unexpected notification action identifier!")
    }
    completionHandler() // per developer documentation, app will terminate if we fail to call this
}

Go ahead and try it out now (you may want to pass a lower value to dateByAddingTimeInterval: for testing purposes).

iOS Simulator Screen Shot Feb 6, 2015, 1.25.36 AM

We’ve covered all of the non-geographic functionality of UILocalNotification and now have a pretty full-featured to-do list app, so this concludes our series. You can download the full source code for this project from here.


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

Subscribe via RSS

Core Animation Swift Tutorial – Animatable Properties

This tutorial series requires a basic understanding of UIView hierarchy. If you are new to iOS development, you might want to begin with Developing iOS Apps Using Swift first.

When you first hear about Core Animation, you might think it is all about animation. However, animation is only a part of this framework. Core Animation is an infrastructure for compositing and manipulating visual content onscreen. It uses the GPU to accelerate the rendering of on-screen objects. It divides the content onscreen into individual objects called layers, and arranges them in a tree hierarchy (known as the layer tree). You are familiar with the UIView tree hierarchy, which is built upon the layer tree. In other words, Core Animation accounts for everything that you see onscreen.

In the first part of this tutorial series, you are going to learn the basics of CALayer, how to use its properties to create neat visual effects easily, and at the end of this tutorial, you will learn the most important subclass of CALayer – CAShapeLayer.

What Is CALayer?

CALayers are rectangular objects that can contain visual content. They are stored into a tree hierarchy, and each manages the position of its children sublayers.

Sound familiar? You may say, “It’s like UIView!”

That’s true, but it’s not just a coincidence. Every UIView has a layer property known as the backing layer, which is an instance of CALayer. UIView just ensures the corresponding back layers are correctly connected in the layer tree when subviews are added or removed from the view. It is the backing layer that is responsible for display and animation of the view. The only major feature that the backing layer does not handle is user interaction.

For the purposes of going through this tutorial, I recommend creating an empty single-view iPhone application.

Exploring CALayer

Creating a CALayer instance is very straightforward.

let redLayer = CALayer()

We can set its frame, backgroundColor, and add it to a superlayer just like we do with UIView.

redLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
redLayer.backgroundColor = UIColor.redColor().CGColor
layer.addSublayer(redLayer)

Add this code to a function called setup in the file ViewController.swift, and call the method from viewDidLoad. You should have something like this:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setup()
    }
    func setup() {
        let redLayer = CALayer()
        
        redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
        redLayer.backgroundColor = UIColor.redColor().CGColor
        self.view.layer.addSublayer(redLayer)
    }
}

Notice that the backgroundColor property of CALayer is a CGColor instead of UIColor.

You can also set an image as the content via the contents property. contents is also known as backing image.

We’ll use an image of a butterfly:

Note that you’ll have to drag your image named ButterflySmall.jpg in to your Xcode project hierarchy in order for the UIImage() command to find the picture.

func setup() {
    let redLayer = CALayer()
    
    redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
    redLayer.backgroundColor = UIColor.redColor().CGColor
    self.view.layer.addSublayer(redLayer)
    
    
    let imageLayer = CALayer()
    let image = UIImage(named: "ButterflySmall.jpg")!
    imageLayer.contents = image.CGImage
    
    imageLayer.frame = CGRect(x: 0, y: 100, width: image.size.width, height: image.size.height)
    imageLayer.contentsGravity = kCAGravityResizeAspect
    imageLayer.contentsScale = UIScreen.mainScreen().scale
    
    self.view.layer.addSublayer(imageLayer)
}

contentsGravity is a constant that specifies how the layer’s contents are positioned or scaled within its bounds.

contentsScale defines a ratio mapping between the size of the layer (measured in points) and the size of the bitmap used to present the layer’s content (known as backing image, measured in pixels). The default value is 1.0. Normally we set the value as the scale of the image, as shown above. However, when working with image that are generated programmatically, or image that missing @2x/@3x suffix, you will remember to manually set the layer’s contentsScale to match the screen scale. Otherwise, you will get a pixelated image on your device.

Corners and Border

CALayer has a property called cornerRadius, which applies rounded corners to the layer’s background and border. When the masksToBounds property is set to true, the layer’s backing image and sublayers are clipped to this curve.

On our redLayer, let’s apply some rounded corners, and make the border visible.

func setup() {
    let redLayer = CALayer()
    
    redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
    redLayer.backgroundColor = UIColor.redColor().CGColor
    
    // Round corners
    redLayer.cornerRadius = 25
    
    // Set border
    redLayer.borderColor = UIColor.blackColor().CGColor
    redLayer.borderWidth = 10
    ...

borderWidth, borderColor defines the width and color of a layer’s border.

Drop Shadow

There are four properties that you could configure the drop shadow for a layer, shadowOpacity, shadowColor, shadowOffset and shadowRadius . shadowRadius controls the blurriness of the shadow. A larger value could create a softer shadow that looks more natural.

Let’s add a shadow to our redLayer as well.

redLayer.shadowColor = UIColor.blackColor().CGColor
redLayer.shadowOpacity = 0.8
redLayer.shadowOffset = CGSizeMake(2, 2)
redLayer.shadowRadius = 3

Unlike the layer border, the layer’s shadow derives from the exact shape of its contents, not just the bounds and cornerRadius. However, if you know what the shape of your shadow would be in advance, you could specify it via shadowPath (an instance of CGPath). You could improve performance by doing this.

Animating Layers

Now that we’ve covered a few of the types of properties that are present in Core Animation, let’s quickly walk through creating some actual animations.

// Create a blank animation using the keyPath "cornerRadius", the property we want to animate
let animation = CABasicAnimation(keyPath: "cornerRadius")

// Set the starting value
animation.fromValue = redLayer.cornerRadius

// Set the completion value
animation.toValue = 0

// How may times should the animation repeat?
animation.repeatCount = 1000

// Finally, add the animation to the layer
redLayer.addAnimation(animation, forKey: "cornerRadius")

Here we create a new CABasicAnimation for the cornerRadius property. Run your app and take a look, cool right?

You could just as easily make this animation apply to any other animatable property of CALayer. Try swapping the “cornerRadius” keyPath value in the CABasicAnimation() constructor with the value “borderWidth”. What happens? What about a value of “shadowRadius”?

From this tutorial you can see how the basics of Core Animation work. In the next part we’ll move on to learn about more animatable properties, and how to work with masks to do really nifty effects.

Full code for this tutorial available here, on Github.

Want to get notified when the next part is out? Be sure to subscribe to our newsletter here.


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

Subscribe via RSS

Drawing Custom Views With Swift – Andrew VanWagoner

One of my readers, Andrew VanWagoner, decided to put together a short tutorial that follows up the iOS 8 Swift Tutorial I put together last month. So I’m sharing it here with you all! Andrew is a Senior Software Engineer working for Adobe in Lehi, Utah. You can find out more about Andrew at his site, http://thetalecrafter.com/ or follow him on Twitter at @thetalecrafter.

In Jameson Quave’s Swift tutorial he issued a challenge to use an image rather than Unicode character for a play and pause button on a table cell. Having most of my experience as a web engineer, my natural choice for an icon like this was SVG. After quite a bit of pain, and determination to not just use a raster image, I discovered another scalable alternative.

Trying to load an SVG image

I added a “play.svg” file to my project, replaced the UILabel with an UIImageView, and added the following to my controller.


playIcon.image = UIImage(named: "play")

Well, the first try didn’t work at all, even though Xcode will correctly identify the SVG file as an image and display a nice preview of it. When you run your app, all you get is a blank nothing.

So I took to Google to figure out what I had done wrong. To my surprise, UIImage doesn’t know how to deal with SVG natively. There are multiple projects that try to add support, but I’m still new to iOS in general, so building and including a 3rd party library took me a long time, and I couldn’t quite get it to work. And in my specific use case, most of the SVG features these libraries implement would go unused.

Along the way I found out that you can do drawing primitives using Core Graphics. Since these drawing functions are conceptually the same as SVG path instructions, and even more similar to 2d canvas drawing, this sounded much easier to me. It is also what the 3rd party SVG libraries do under the hood.

Creating a custom view

If you are following along with Jameson’s tutorial, replace the playIcon UILabel with a View in the storyboard, and in the Identity Inspector, give the view a Class name “PlayPauseIconView”. Then create a new Swift class file “PlayPauseIconView.swift”. We’ll need to import UIKit, and the class needs to be a subclass of UIView. We will want to use this same view to draw both the play and pause icon, so let’s add a member that keeps track of which one we should draw.


import UIKit
class PlayPauseIconView: UIView {
  var isPlaying = false
}

In order to do custom drawing, we override the drawRect function. Just to keep things clean, put the code for drawing the play and pause icons into their own functions. Stub them out for now.


override func drawRect(rect: CGRect) {
  let context = UIGraphicsGetCurrentContext()
  if isPlaying {
    // already playing, so draw a pause icon
    drawPausePathTo(context, boundedBy: rect)
  } else {
    // not playing, draw a play icon
    drawPlayPathTo(context, boundedBy: rect)
  }
}
func drawPlayPathTo(context: CGContextRef, boundedBy rect: CGRect) {
}
func drawPausePathTo(context: CGContextRef, boundedBy rect: CGRect) {
}

You’ll notice that in order to draw, we need a context to draw inside of. We can get the current context with UIGraphicsGetCurrentContext(). Then we decide which icon to draw and call the appropriate function.

For the play icon we want to draw a triangle in the middle of our view, taking roughly half of the visual space. To get a list of all of the drawing functions available to use, you can look at the CGContext Reference.

First we will set the color we want to draw, then guide the turtlecursor across the path we want, then fill in the shape. Using the provided rectangle as our point of reference, we can size our triangle to scale with the resolution of the device.


func drawPlayPathTo(context: CGContextRef, boundedBy rect: CGRect) {
  CGContextSetFillColorWithColor(context, UIColor.blackColor().CGColor)
  CGContextMoveToPoint(context, rect.width / 4, rect.height / 4)
  CGContextAddLineToPoint(context, rect.width * 3 / 4, rect.height / 2)
  CGContextAddLineToPoint(context, rect.width / 4, rect.height * 3 / 4)
  CGContextAddLineToPoint(context, rect.width / 4, rect.height / 4)
  CGContextFillPath(context)
}

Again, if you are familiar with other 2d drawing systems, including SVG path notation, this is pretty straightforward. Using the same Core Graphics functions, fill out your pause icon function.


func drawPausePathTo(context: CGContextRef, boundedBy rect: CGRect) {
  CGContextSetFillColorWithColor(context, UIColor.blackColor().CGColor)
  CGContextMoveToPoint(context, rect.width / 4, rect.height / 4)
  CGContextAddLineToPoint(context, rect.width / 4, rect.height * 3 / 4)
  CGContextAddLineToPoint(context, rect.width * 2 / 5, rect.height * 3 / 4)
  CGContextAddLineToPoint(context, rect.width * 2 / 5, rect.height / 4)
  CGContextAddLineToPoint(context, rect.width / 4, rect.height / 4)
  CGContextFillPath(context)
  CGContextMoveToPoint(context, rect.width * 3 / 4, rect.height / 4)
  CGContextAddLineToPoint(context, rect.width * 3 / 4, rect.height * 3 / 4)
  CGContextAddLineToPoint(context, rect.width * 3 / 5, rect.height * 3 / 4)
  CGContextAddLineToPoint(context, rect.width * 3 / 5, rect.height / 4)
  CGContextAddLineToPoint(context, rect.width * 3 / 4, rect.height / 4)
  CGContextFillPath(context)
}

Now that we have our custom drawing code, update cell.playIcon.text calls to instead set isPlaying on your PlayPauseIconView. Go ahead and run your app, and check out your custom drawn icon.

You’ll probably want to fine tune the size of your view in the storyboard. I ended up with a 22×22 square.

You may also find that your icon isn’t updating properly when you start previewing a song. Core Graphics tries to draw your view once, and just keep reusing the already drawn layer, so it needs some way to know that your view has changed and needs to be redrawn. Setting isPlaying isn’t enough. Luckily UIView has a function that does exactly that.


setNeedsDisplay()

To make sure we always call this function when we change the isPlaying state, either add a didSet listener to isPlaying, or just add functions that set isPlaying and then call setNeedsDisplay. Make sure to update your controller to use the functions instead of directly setting isPlaying.

That’s it. You should now have a custom view that draws your play and pause icons, and it properly updates when you start and stop previewing a song.

If you are more ambitious, you can add a spinner for while the song is loading from the network, and put your pause icon in the middle. You can see how I did it in my project on github.

Update: The repository for this section has been updated to reflect the changes to Swift in Xcode Beta 3/4. The beta also adds access controls, which you can learn about here.


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

Subscribe via RSS