Join Lee Brimelow for an in-depth discussion in this video Creating a Today widget, part of iOS 8 SDK New Features.
- So the first type of extension we're going to look at creating is the today extension, or today widget, as it's also known. Here I am on the simulator. I just want to refresh your memory about what the today screen actually is. So when we pull down notifications, we have this "Today" tab, and this is showing me anything important which is coming up in my day today. So here we have a Calendar view. I don't have much going on on this day. But you can see, in iOS 8 we now have an "Edit" button.
And this allows me to add custom widgets to this Today screen, which we're going to create right now. So I've opened up the Today extension starting project. And it's a simple iOS application. Here you can see the main story board. I have a view controller with a text field, and then a button below it which says, "Set Today Message." If we look in the view controller, I have an IBAction method that's actually connected to that button. So to add an extension to our iOS application we need to add a new target.
So in the project settings, under "General," I'm going to click the plus sign to add a new target that's going to be a type Application Extension, and we're going to choose "Today Extension." And I'll give this a name of "TodayWidget." And click Finish. So it's done a couple of things now. It's created this new target. It's created a new run scheme here, called "TodayWidget." And it's also created a template project for my extension in this TodayWidget folder.
It has a ViewController, and it has a main interface storyboard. So if we look at the interface for the extension, it's very simple. It's simply a label that says, "Hello World." So now let's just modify this a little bit. So let's actually left-align this text. And we'll set the color to be white. And now I want to add a reference to this, inside of my today view controller. So I'm going to open the assistant editor. Let me actually just get a little more space.
And here is the ViewController that the extension template created. You can see it simply is a subclass of UIViewController. And it's implementing the NCWidgetProviding protocol. But I'm just going to control-drag, so I have an outlet for this. And I'll just call this, "message." And I can close the assistant editor. Now what we need to do is to set up the communication pipeline between my main app and the extension. And to do that we use something called, "App Groups." So I'm going to come to my project again.
First go to the TodayExtension app target. And go to Capabilities, and I'm going to go down to App Groups, and I'm going to turn on that entitlement. Now it's going to ask you here for your development team to use for provisioning, so I'm assuming you already have all of this stuff set up. If not it will ask you to do that. I'm going to choose it. And you can see here it's actually going to list any App Groups that you have already defined. Now obviously, as I was creating this sample, I have already created an App Group called, "group.com.leebrimelow.today." Now this has to be a unique name, and unique in the sense of all applications that are out there in the world.
So again, a reverse domain name type syntax is probably the safest bet. So now you can just create a new App Group by clicking on the plus sign. Now down below here, depending on your project and your signing certificates, it may ask you to fix some issues, like it does in other areas when we're adding capabilities. So just go ahead and fix any issues that you have that come up. So now that we've turned on this App Group for our main application, we also need to turn it on for our TodayWidget extension.
I'm going to turn that on. I'm going to choose my account. And I'm going to check off that same App Group. So again, obviously, it has to be the same App Group name. And what this is going to allow, it's going to allow us to write to a user default using that specific App Group name. Okay, so I'm going to go to my ViewController. This is in my main application, where I have the setMessage action method. And this happens when the user is going to click on that setMessage button.
So what I want to do is to get a reference to the user defaults area of my App Group. So I'm going to create a constant. I'll call it "defaults = SNUserDefaults?" And then we want to use the suiteName constructor, and here we're going to pass in the string name of our App Group that we just created. And in my case it's "group.com.leebrimelow.today." So now that we have that user default area set up, now I can actually set an object in that user default based on the text, which is in that message field.
So what I'm going to do is to say, "defaults." We'll just force-unwrap this. And we're going to call the "setObject" method. Now for the value here what I want to pass in is the text in that message field. I'm going to say, "messagefield.text." And then for a key, well we'll just hard-code it here. The key will be, "TodayMessage." So essentially when the user is running the actual iOS application, they can type in a message, click the "setMessage" button, and it's going to set a value in this shared UserDefaults area that can be read by the extension.
So I'm going to copy this line, because we're going to use it over in the extension as well. So now I'm going to go to my ViewController in my extension. And you can see we have this method called, "widgetPerformUpdateWithCompletionHandler." So this is essentially going to be called any time the extension decides that it needs to check for updated data. Essentially this could be when the user swipes down and shows the today screen, this method is going to be called.
And essentially in here we need to check to see, is there an update and should we actually update the extension UI? So now you can see here in the comments that were put in, by the template, we need to return one value from the NCUpdateResult class. And it could be Failed, if there was an error, NoData, and that's if we've decided that it doesn't need an update, or NewData, if we want an update. Let's just get rid of these comments to give some space. And I'm going to paste in that default constant, because again, we're going to need a reference to it here as well.
Okay, so what I need to do now is, I need to make sure there is an actual value in the UserDefaults for this key. Because, only if there's a value do I actually want to update the UI. So I'm going to create an if statement. I'm going to say, "if let messageText = defaults?" and we're going to use optional chaining here which is basically saying only continue if defaults that I created above is actually valid. And we're going to call the objectForKey method, and give that key name that we created, which is, "TodayMessage." Now you can see here, it's giving me a warning.
And to get rid of that I just need to type this to AnyObject. So now I know if I get into this if block, then there is actually a value and I should update the UI of my extension. So to do that, I'm going to go to that message label, set the text property, "= messageText as String." Now you see here it's also giving me a warning again. It's giving me two fixes. I can use a question mark on as, or add parentheses around the cast, which I'll do.
So again, any time the system determines that we should check for an update to the widget, it's going to call this function. We're going to check in the user defaults. If there is actually a value for that key, if it is we're going to set the UI of our extension. Okay, so let's go ahead and run this. Now it's important to note, here we have options, because we can run the actual extension, or we can run the application. Now running the application is going to install the extension, but we will be debugging the actual application.
If we choose the TodayWidget, it's going to actually debug the extension itself, which we're going to choose. So I'm going to run this. And now, you'll notice that it's asking me which application do I actually want to run this extension in? Which application do I want to debug it in? And obviously we're going to choose the Today application. And I'm going to run this, let it compile. And now you can see the Today screen is open and we have this "Hello World" message.
So now what we want to do is to actually go to the application. Now we can type in a new message. Let's just say, "This is cool." And click, "Set Today Message." And now if we come back to our extension, we can now see that that message has updated, because, again, both the extension and the app are able to talk to this shared user default section. Now this is just the tip of the iceberg with today widgets. We can add list views, table views, any type of UI control or widget into it.
But this will at least get you started with creating a today widget that communicates with its containing application.
- Getting started with Swift
- Using Swift playgrounds
- Exploring unified storyboards
- Understanding how extensions work
- Creating a Today widget
- Using the Photos framework
- Controlling graphics with the Metal framework
- Overview of HealthKit and HomeKit
- Combining Swift and Objective-C