iOSDevCamp Colorado was a couple of weeks ago and I did a presentation/demo on iOS app extensions. I wanted to focus on how to actually do things, so mostly I worked in Xcode rather than present from slides. But rather than paste code in as I went or (gasp!) try to do it live, I worked from a git repository I had built while developing the demo app. Every time I made a significant change, I’d commit it. During the presentation I would show some of what I was doing at each step and then jump ahead to the next commit to show the completed version. I like the idea but I need to work on doing it smoothly.
One nice thing about it is that I can share the project and have it provide more information on the development process. Rather than just getting a demo app presented as a fait accompli, you can check out each step along the way to see how it came together.
The code is on GitHub. For reasons I won’t go into right now I ended up re-doing the project so that it’s slightly different than what I used at iOSDevCamp.
The starting point is an extremely basic note-taking app. I modeled this somewhat on Apple’s Lister demo app, but I kept it simpler. Most importantly I dropped Lister’s dependency on iCloud, which can make it hard to get working. The repo then goes through the steps of adding a couple of different kinds of extensions, ways to share code and data between an app and its extensions, and a few other useful things.
The repository builds on the basic notes app via the following steps (links are to the corresponding commit at GitHub):
- Today extension template. Add a today extension target in Xcode, but don’t make any changes to it. This creates a working today extension with a default “Hello, World” UI.
- Fix today extension name. Change the display name for the today extension to be more descriptive when it appears in the iOS notification center. The only significant change is to
Info.plist
. - Build today extension UI. Replace the default today extension UI with a table view UI to match the app. This UI is non-functional at this point, since there’s no code to support it yet.
- Add a custom framework to hold code that will be shared between the app and the extensions (in this case only the
DemoNote
model object). Change the app to#import
the framework header instead of the shared code header. - Enable app groups to share data. There are no code changes here, but there are a couple of news entitlements files and corresponding project file changes. These correspond with Xcode communicating with Apple’s developer center to configure the app ID and generate custom provisioning profiles that include the new entitlements.
- Use the app group in the app. Change the existing app code to read/write data via the new app group instead of using its private documents directory.
- Add today extension code. This adds code similar to the app to load and display notes. It also adds the shared framework to the today extension and– importantly– tells Xcode that the framework should only use extension-safe APIs.
- Add a URL scheme so extensions can call back to the app. The URL scheme is in the app, and can be used by app extensions. In this case it’s used by the today extension to launch the app and to tell it which note the user tapped on. The app displays that note.
- Share extension template. Add a share extension target in Xcode, but don’t make any changes to it. This creates a share extension which uses
SLComposeServiceViewController
to display a UI similar to the built-in share extensions for Twitter, Facebook, etc. - Add share extension code. This adds a JavaScript preprocessing file which is used when the share extension is invoked from Safari. The JS code extracts various items of interest, such as the selected text. The app extension code looks through this data to fill in the new note text. The default share extension UI is still in place.
- Keep data synchronized. Update the app to make it more aware of the share extension. Since new notes can now be added from outside the app, the app needs to be sure to check for new notes when the app enters the foreground.
- Custom share extension UI. Replace the standard
SLComposeServiceViewController
-based UI with a custom UI. Partly because there’s no API to change the text on the share UI’s “Post” button, and partly just because I can. It’s still a share extension, though it’s not using the standard UI anymore. - Better data synchronization. Update file reading/writing to use coordinated reads and file presenters to keep app and extension data in sync. With this change the app doesn’t check for changes when coming to the foreground. Instead it’s notified that changes are available, thanks to
NSFilePresenter
.
[This isn’t a complete list of commits. It leaves out some bug fixes that aren’t directly relevant to demonstrating app extensions. Check out GitHub if you want to see everything.]
I’ll be writing more about some of this soon. If there are any areas you’re particularly interested in, contact me on Twitter.