Extensions in iOS8: Shared Storage

by Louis Tur

The fibonacci sequence is a pretty neat sequence of numbers. You may know them as the string of numbers were the next number is obtained by adding the previous two. This ends up looking like: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] ...(and so on) and being expressed as Fibn = Fib(n-2)+Fib(n-1). Nothing too jazzy, but a classic hurdle on the way to learning the basics of algorithms and, usually, recursion. But the fibonacci sequence exists in nature, music, and pop culture.

Very recently, it's been making its rounds in the form of 3D printed sculptures that appear "alive" when rotated and recorded at a very low shutter speed. And it really is pretty amazing to watch and then subsequently read about. If you haven't watched it already, it's worth your time (via Vimeo):

Though, as it turns out, what isn't interesting about the fibonacci sequence is anything related to trying to create a cute application that does it in some novel way. But maybe that's just me. So, after two weeks of it giving me coder's block, I decided to table it altogether, and instead I have something far more interesting and equally frustrating...


Share Extensions (iOS8/Xcode6)

What are Share extensions? They're what allows you to quickly post to social media sites (or do other things, socially) from iOS apps. Any app can define if it wants to use a Share extension, and if its declared to handle the appropriate attachment types, the option appears from your sharing options. Below I show two examples of where they can be found and what they look like.

Pressing the share button brings up a UIPopoverController(?) that gives you a number of ways to share the content from the page/app your in. That content may be links, images, twitter posts, etc...

Selecting one of the social media options, results in either a SLComposeServiceViewController (if you're creating a custom share option for your app) or a SLComposeViewController (if you go with a built-in 3rd party app, i.e Facebook, Twitter, Weibo).

And truly, out of the box, the SLComposeViewController and it's sibling, SLComposeServiceViewController (both are subclassed from UIViewController, but they aren't subclassed from one another) provide a really easy way to share content exactly like Apple does. The tricky part is if you want to do some customization and use a shared data store.


Beginning

The Apple documentation on Share extensions, and extensions in general, are actually pretty decent. But as I'm finding out, its the subtext of the documentation that requires actual analysis (see App Groups, Entitlements, Uniform Type Identifiers, and why the hell not? some Toll-Free Bridging). I used a Ray Wenderlich tutorial to get as far as adding the extension as a new target... but then it got really wordy and I decided I'd rather just do it myself. But if you like tutorials, Ray has some of the best. Though to follow along, you'll need the following:

  1. An existing, or completely new Xcode project
  2. Add a new target to the project, of type Share Extension
  3. A data type to share (URL is easy, so I used that).

Pivoting

Creating the extension in Xcode nets you a few things: a new extension target, a boilerplate copy of the SLComposeServiceViewController, and a nib that can represent the SLComposeServiceViewController's view. The header file for the view controller is just this:

#import <UIKit/UIKit.h>
#import <Social/Social.h>
@interface ShareViewController : SLComposeServiceViewController;
@end

And although it doesn't say this explicitly anywhere (that I saw), if you want to start using the nib or your own views/controllers, you can just change the interface such that it inherits from UIViewController instead of SLComposeServiceViewController. Which is exactly what I did. The image below shows what the default implementation looks like vs. what the custom one I've just started will look like (in this case, using the nib).


Sharing Data between Extension and App

This part isn't that difficult, really. But the annoying part of this is that the Apple extensions guide links to a page on how to set up app groups but the link is a quick blurb that more closely related to OSX set up. In fact, the guide itself is a bit nebulous on how to do this entire set up, but fortunately I came across the perfect resource to get this going.

So, what you'll do is:

  1. Log into https://developer.apple.com
  2. Go into Certificates, Identifiers & Profiles
  3. Select App Groups on the left menu.
  4. Then click on the + symbol
    • Give the app group a description (for your benefit) and an identifier to use in your app.
    • The identifier is written in reverse domain notation and should include the name of your company and app (ie. com.apple.FaceTime)
    • The "group" identifier will prefix any thing you write (ie. group.apple.FaceTime)
  5. Go into XCode, into your project settings
  6. Enable App Groups from the Capabilities tab
  7. The app group you've just registered should appear on the list presented, so just make sure its selected
  8. Do this for both the main app target and the extension target!

As a result of the above, Xcode will generate two entitlements plists that contain information that will allow your extension and app to communicate and share data between them. All it really is (in this implementation) is a single key/value store with a key of com.apple.security.application-groups and a value of an array of your registered app groups. Though this will also let you do neat things like starting an NSURLConnection task from your extension that gets placed on a background queue and picked up by your main app.


Shared NSUserDefaults

The app group set up allows for shared data between extenstion and its container app, and as you might imagine this includes a few data store options: NSUserDefaults, CoreData, and Keychain services.

For NSUserDefaults, it's as simple as instatiating an NSUserDefaults object that specifies a app group identifier. The following sample illustrates this point:

//... defining a constant for later use
static NSString * const kSharedAppDomain = @"group.SRLabs.sharedData";

//... later in code
NSUserDefaults * sharedAppDefaults = [[NSUserDefaults alloc] initWithSuiteName:kSharedAppDomain];
[sharedAppDefaults setURL:self.videoURLToSave        
                   forKey:self.videoURLToSave.absoluteString];

//... when you're ready to save
if ([sharedAppDefaults synchronize]) {
    NSLog(@"Bookmark syncronized to NSUser");
}

And you would instantiate this NSUserDefaults instance in both your extension and container app. Simple as that!


Next Up: Custom Views and Data Extraction

Now that we can save information between our extension and container app, the next steps will be to extract relevant, "shareable" data from the host application.

Part of this will involve custom implementation of the extension view itself, as we're going to define how to handle that data in a way that makes sense for my design.

Lastly, full code will be posted at a slightly later time.

>catthoughts

Louis Tur

"How" has been the single most used word in my literary arsenal for as long as I can remember. I've never really been satisfied knowing that something works, but only by knowing how it works.

Read more from this author