Building a Today Extension in iOS 8
iOS 8 provides some incredible new features that apps can take advantage of. However, due to the sheer amount of new features and frameworks Apple released this year, there was bound to be bugs and issues. The Today view extensions are no exception. I was inspired by the Tumblr team’s post on issues with Share extensions to share the bugs and pitfalls you will face when building a Today Extension Widget.
Today widgets are meant to provide quick glanceable information. At WWDC we saw the example of ESPN’s sport scores. It has no buttons or settings. You configure favorite teams to track in the app, and the Today Widget is a simple summary view. Social networks may show recent communication, financial apps may show a summary of your accounts, and productivity apps may show recent to-do items. If your app has some simple information that users check frequently, a Today Widget might be a great feature to add.
I started to build a Today widget for my app Terrific, which shows you great nearby businesses when you need to quickly find a restaurant or shop. I figured a list that showed nearby restaurants would be useful at a quick glance.
The widget will get the users location and perform a simple MKLocalSearch to find nearby places to eat. It uses notificationCenterVibrancyEffect to achieve an eye-catching visual and adjusts preferredContentSize to hide or shrink the widget if there are no places nearby.
It was simple to add the ability to to open the maps app to get directions and details about the business. What wasn’t so simple was how to persist the data and how to present the data in a clean and consistent way.
Problem #1: Understanding UIVisualEffectView
The fancy blur and backlit text effects in Notification Center don’t happen by accident. With iOS 8, we have access to a new class called UIVisualEffectView. I fought with this for a bit, because I didn’t realize there was a notificationCenterVibrancyEffect class method in a NotificationCenter category. The other trick with UIVisualEffectView, is that views that should be blurred/vibrant must be added to the visualEffectView.contentView, not the other way around.
The restaurant names in the GIF are UIButtons added to a visualEffectView.contentView.
Problem #2: This is Not Your Normal View Controller
// Instead, widgets should load cached state in ‘viewWillAppear:’ in order to match the state of the view from the last ‘viewWillDisappear:’, then transition smoothly to the new data when it arrives.
With a normal view controller, UI is setup in viewDidLoad and handle animations and loading data in viewWillAppear. Not so with a Today view controller conforming to the NCWidgetProviding protocol.
Now I know to reload the view in viewWillAppear. What I didn’t realize is that the viewController is deallocated each time it is dismissed, so it can’t persist data in a NSArray property. Since this is a simple app, I thought I would just use NSUserDefaults, but its not as simple as it seems.
Problem #3: Standard User Defaults Don’t Persist Between App & Extension
I already have a system storing important data in NSUserDefaults in my app to configure how the app should search. I assumed that [NSUserDefaults standardUserDefaults] would return the same thing for both the app and extension, but not so. This great article informed me that I need to create a group in the iOS Developer Portal, then initialize NSUserDefaults with a suite name of the group, i.e. [[NSUserDefaults alloc] initWithSuiteName:@”group.com.mycompany.myapp”].
Problem #4 No Keyboard Access & More
As much as possible, let users perform a task or open your app with a single tap (note that the keyboard is not available within a Today widget).
This is not so much a problem, as an intentional limitation. Today widgets are meant to be quick to view and interact with, so typing is right out. I had intended to provide a quick search box, but found that this feature was impossible. Don’t try and use SLComposeViewController, MFMailComposeViewController, or any other UI that requires text entry. For the full list of API that have “NS_EXTENSION_UNAVAILABLE” defined on them, see this Gist.
Problem #5 What Should Today Widgets Be?
Within hours of iOS 8's public launch, someone started a tumblr called “Shitty iOS 8 Widgets” pointing a finger at apps that had less than useful widgets. While I don’t condone “design shaming”, I want designers to be intentional about what makes a Today view useful. Not everyone wants a today view that shows motivational quotes, but Today views are opt-in. A user has to add it manually. Those who need a little motivation can put the view right at the top and those who are self motivated won’t add it at all.
From the “App Extensions” section of the iOS Human Interface Guidelines:
People visit Notification Center to get brief updates or to perform a very simple task, so it’s best when your Today widget displays the right amount of information and limits interactivity.
Giving users a way to open your app from your Today widget can work well, but it’s essential that you continue to provide useful, timely information in the widget. People may not appreciate a Today widget whose only function is to launch your app.
As usual, Apple gives some pretty specific guidelines about how a Today view should behave and appear. These are just guidelines however, and there are possibilities to make much more complex Today views, such as PCalc:
PCalc provides a full calculator in the Today view. It breaks guidelines all over the place! It is too tall, too many buttons/taps, and doesn’t provide quick glanceable information. It has a nice use of the vibrancy effect, while making the button text a solid white color. It is definitely a tool meant for power users. Yet Apple has approved it and even featured it in the “Extend Your Apps” section. PCalc got lucky. Some apps with advanced features were not so lucky.
Problem #6 What Can Today Widgets Be?
As usual, we developers have to live in a world where Apple makes the rules. Apple is still deciding what should and should not be in the Today View. A great app called “Launcher” was approved in the store and saw great success, only to be rejected a few days later.
Apple is still inconsistent about what will be approved. Another app called “Launch +” is still live on the app store. It will take some time for developers to have confidence about what is acceptable Today widget behavior. I am a bit worried my widget may be rejected as it opens an app other than itself (Maps).
I will submit Terrific in its current state and see if it is accepted. It is still the early days for iOS 8. I am sure Apple’s app approval rules will change as new apps create today widgets.
Demo of the current progress on the Today Widget:
The ‘Terrific’ sample project is on Github here: https://github.com/jstart/Terrific
For a deeper dive on debugging and radars with today widget extensions, see this article on Atomic Bird.
Follow me @iAmChrisTruman for updates on my work with Today Widgets.