Using App Extensions for iOS 8: Today Extension

* This tutorial uses Swift * App Extension feature is available post iOS 8 *

What are App Extensions?

App extensions basically extends your app beyond itself. In other words it will allow user’s to view items/perform actions in your app while using other apps or system apps.
Confusing eh? Read here.

App Extensions Increase Your Impact

Apple: App Extension Programming Guide

There are several types of App Extensions known as Extensions Points as described by Apple in the above link. App Extensions are cool additions to any iOS app. Based on various features of your application, you can incorporate one or more kinds of extensions to your app.

In this tutorial we will see how to add the Today extension AKA the Today Widget to an application. (The Today Widget appears in the Today view of the Notification Centre.)

Implementing the Today Widget App Extension

For this demo, we will create an application which will generate a random quote by some of the famous personalities and display it in the App and of course in the Notification Centre as a Today Widget!

Creating the App:

1. Creating a simple iOS application
First we need to create an application for which we can use the single view application template in Xcode.
(In Xcode, go to File -> New Project and choose iOS -> Application -> Single View Application)

2. Including the plist database to the application
To have a database of different quotes to pick from and display in the application, add random quotes into a plist of Array and Name it Thoughts.plist. The application will then read and display a quote randomly from this file.
Check out the demo project for the sample plist.

iMotivate_0

3. Adding code for displaying a random quote
Update the viewDidLoad method of your viewController as below to display a random thought for the day. Add a UITextView in the storyboard and connect it to the outlet quoteTextView which will be set the value.

class ViewController: UIViewController {

    @IBOutlet weak var quoteTextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let filePath: String = NSBundle.mainBundle().pathForResource("Thoughts", ofType: "plist")!
        let thoughts: NSArray = NSArray(contentsOfFile: filePath)!
        let numberOfThoughts = UInt32(thoughts.count)
        // Generate a random number within the number of thoughts.
        let randomThoughtNumber = arc4random_uniform(numberOfThoughts)
        // Set the thought for today.
        let randomThought = thoughts.objectAtIndex(Int(randomThoughtNumber)) as! String
        self.quoteTextView.text = randomThought
    }

}

Now, run the application and verify if it is displaying a random thought for the day.

iMotivate_1

Adding the Today Extension to your application:

1. Adding an extension target to the application
Add a new target to the project of type extension.
In Xcode, go to File -> New -> Target

iMotivate_2
Select iOS -> Application Extension -> Today Extension

iMotivate_3 Provide a Product Name and Active the extension if prompted.
(Make sure to verify that this target is added to your current project
Project: <your project name>
Embed in Application: <your application name>)

iMotivate_4

2. Verifying the new extension target:
In the Xcode you will find the target and a new group in the project navigator.

iMotivate_5
In the scheme selection, select the extension target and Run. Select Today when prompted to choose the application to run.

iMotivate_6
The Today Extension template includes a sample UI which displays “Hello World” when you run it in the Notification centre under Today.

iMotivate_7

3. Customising the Today Widget
a) Including the the plist database to the newly added extension target
(Drag and drop the file and select the the extension target under Add to Targets in the popup for adding the file.)
(If you have already added the file to another target in the project as in step 2 in Creating the Application,
Select the Project in Xcode project navigator. Select the added target for the extension. Go to Build Phases -> Copy Bundle Resources tap on ‘+’ button and select the Thoughts.plist and select Add.)

b) Updating the UI
Select the MainInterface.storyboard under the extension target.
Adjust UI and add a referencing outlet of the UILabel to the extension viewController so that we can set the value for the label through code dynamically.

iMotivate_8

c) Adding code for reading and displaying a random thought
Add a variable for reading a random quote string. Referencing outlet for the label say “quoteTextLabel” and update the viewDidLoad method as below to read a random quote from the plist.
And as per the Apple’s Human Interface guidelines, the widget is updated in the viewWillAppear method. The class should look like below after the changes.

class TodayViewController: UIViewController, NCWidgetProviding {

    var randomThought: String = "What man actually needs is not a tensionless state but rather the striving and struggling for some goal worthy of him. What he needs is not the discharge of tension at any cost, but the call of a potential meaning waiting to be fulfilled by him. -Viktor Frankl" // Setting a default value for the variable.

    @IBOutlet weak var quoteTextLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view from its nib.
        let filePath: String = NSBundle.mainBundle().pathForResource("Thoughts", ofType: "plist")!
        let thoughts: NSArray = NSArray(contentsOfFile: filePath)!
        let numberOfThoughts = UInt32(thoughts.count)
        // Generate a random number within the number of thoughts.
        let randomThoughtNumber = arc4random_uniform(numberOfThoughts)
        // Set the thought for today.
        self.randomThought = thoughts.objectAtIndex(Int(randomThoughtNumber)) as! String
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.quoteTextLabel.text = self.randomThought
    }

The function arc4random_uniform() generates a random number within the input parameter. In this case a random number from 0 to (numberOfThoughts – 1)

 arc4random_uniform(numberOfThoughts) 

This function takes a parameter of type UInt32 and returns a UInt32 value. To convert the values from and to Int, we use the below functions.

UInt32(thoughts.count)  // Returns UInt32 from Int
Int(randomThoughtNumber)  // Returns Int from UInt32

By default the UI will include margins for the Today Widget’s content view.
If you need custom margins, you can implement the below method by corresponding to the NCWidgetProviding protocol.

    func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {
        // Implement this method and return the edge insets if you want to use custom edge insets.
        return UIEdgeInsets(top: 1.0, left: 3.0, bottom: 1.0, right: 3.0)
    }

d) Build and Run the application
In the scheme selection, select the extension as Target and Run. Select Today when prompted to choose the application to run.
The application should run and the widget should display a random quote. Also, as per the above implementation a new quote will be displayed every time you pull down the notification centre.

iMotivate_9

e) Changing the behaviour of when the quote should be updated
To change this behaviour return the appropriate value in this method.

    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        // Perform any setup necessary in order to update the view.

        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData
        completionHandler(NCUpdateResult.NewData)
    }

f) Opening the containing application when user taps on the widget in the notification centre
To open the containing application from the widget, the containing application should have a URLScheme specified.
To include a URL Scheme for the containing application go through this article: Launching your iOS application from another application using URL Scheme
Say the containing application has a URL Scheme by name “iMotivate”, To open up the application, add a button in the UI of Today extension’s MainInterface.storyboard and link the target for sent action to a method like below and add the call the openURL function on the extensionContext property of the viewController(self) as below.

    @IBAction func launchiMotivateApp(sender: UIButton) {
        // User tapped on the widget. Open the application using the specified URL Scheme.
        let appURL = NSURL(string: "iMotivate://")
        self.extensionContext?.openURL(appURL!, completionHandler:nil)
    }

Congratulations! Your extension is now fully functional 🙂

Here’s the sample code.

Where to go from here?
There are lot more to app extensions. We have barely scratched the surface in this demo. Few interesting things you can do with extensions are as below:

  1. Handling Common Scenarios: Apple: App Extension Reference
  2. Implementing Other App Extension types:

References:

  1. Apple: App Extension Programming Guide
  2. Apple: App Extension: Debug, Profile, and Test Your App Extension

Other Related Tutorials:

  1. Tuts+: iOS 8: How to Build a Simple Action Extension
  2. Ray Wenderlich: Today Extensions Tutorial
  3. Glimsoft: How to create iOS 8 Today extension and share data with containing app – tutorial
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s