Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Swift Introduction to iOS 10: Notifications, Animations and Speech Recognition

DZone's Guide to

A Swift Introduction to iOS 10: Notifications, Animations and Speech Recognition

Today we'll round up the series of iOS 10 features by looking at user notifications, speech recognition, and animations.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

We've covered SiriKit, and we've covered iMessage. In the final part of this short series, we'll cover the rest of the headline features in iOS 10. 

Notifications

Notification scheduling has been given an overhaul in this version of iOS. There are now specific triggers for Calendar, Location, and TimeInterval. You create your notification content, define your trigger and send them to the notification center for delivery. 

For example, a time interval notification can be scheduled as follows 

  let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)

You can also create a notification service provider which acts as an intermediary between the message arriving and its display. This is useful for things like decrypting messages that arrive from the server before they are displayed to the user.  This is done by implementing the UNNotificationServiceExtension, which can be added to your existing project through File/New Target/Notificiation Service Extension 

Image title

The code that generates is as follows. 


import UserNotifications

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"

            contentHandler(bestAttemptContent)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

}

There are also some changes in how notifications are displayed by using the Notification Content extension. 

Image title

When implemented, you are provided with a NotificationViewController and a storyboard to display your own custom content for any notifications received.

import UIKit
import UserNotifications
import UserNotificationsUI

class NotificationViewController: UIViewController, UNNotificationContentExtension {

    @IBOutlet var label: UILabel?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any required interface initialization here.
    }

    func didReceive(_ notification: UNNotification) {
        self.label?.text = notification.request.content.body
    }

}

For more on user notifications: 

Animations

Animating views used to be a cumbersome activity. Instead of relying on third party libraries to make it easier, it's worth checking out the new UIViewPropertyAnimator in iOS 10.

The animator allows you to modify the animatable properties of a view; namely frame, center, alpha, and transform. You then define the timing curve for the speed of the animation, and finally, you can give a completion block to run when the animation has completed. 

You can also modify the timing of your animation, as well as pause, resume and cancel animations. 

The code behind all this is surprisingly easy. Here's a code snippet that illustrates how it all works, by changing the scale of a red square on the screen. First the square is created as a UIView.

  let square = UIView(frame: CGRect(x: 100, y: 100, width: 128, height: 128))
        square.backgroundColor = UIColor.red
        view.addSubview(square)

Next, the animation is expressed, and stored for later manipulation


        // create our property animator to scale up the square
        scaleUp = UIViewPropertyAnimator(duration: 10, curve: .easeInOut) 
{ [unowned square] in
            square.transform = CGAffineTransform(scaleX: 2, y: 2)
        }

The animation is kicked off using the startAnimation function: 


        scaleUp.startAnimation()

You can decide to pause and restart the animation at any time: 

        // toggle between running and not running
        if scaleUp.isRunning {
            scaleUp.pauseAnimation()
        } else {
            scaleUp.startAnimation()
        }


Image title

For more on UIViewPropertyAnimator check out: 

Speech Recognition

Speech to text is now available in iOS10. There are two types:  conversion of audio files to text, and  dictated input from the microphone in real time which is then converted to text.

The steps required to add speech capabilities to your app is very straightforward. AppCoda has put together a great tutorial showing you how to create your own speech to text app, along with all the project source on GitHub.

The core steps in setting things up are as follows: 

  • Import Speech Framework
  • SFSpeechRecognizer handles recognition in a specific locale
  • SFSpeechAudioBufferRecognitionRequest provides audio input to the recognizer
  • SFSpeechRecognitionTask provides result of the request, allows canceling task
  • AVAudioEngine provides audio input

import Speech

class ViewController: UIViewController, SFSpeechRecognizerDelegate {


    private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))!

    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    private var recognitionTask: SFSpeechRecognitionTask?
    private let audioEngine = AVAudioEngine()

Before you can capture audio, you will need to request permission from the user. This is especially important here, as Apple temporarily store the data that is transmitted to improve the accuracy. No doubt this will ruffle some feathers, so make sure to make the intent of your app crystal clear to your user. As with any permission requests, you will need to add a key to your Info.plist file; in this case NSSpeechRecognitionUsageDescription. 

Requesting authorization is done as follows, with four possible status codes from the request

SFSpeechRecognizer.requestAuthorization { (authStatus) in

            switch authStatus {
            case .authorized:

            case .denied:

            case .restricted:

            case .notDetermined:
             }


        }

Limitations 

Speech recognition is a network based service so there are limits that Apple will apply. It's certainly worth being aware of these before you embark on a speech enabled app

  • “Devices are limited by the number of  recognitions that can be performed in one day”
  • “Individual apps may be throttled globally” 
  • “Speech recognition can place a relatively high burden on battery life and network usage” 
  • “Utterance audio duration is limited to about one minute, which is similar to the limit for keyboard-related dictation” 

For more on Speech Recognition in iOS: 

Here's the original talk that I gave on this new set of features in iOS 10 


Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
swift ,ios 10 ,notifications ,animations ,speech recognition ,mobile

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}