Extensions in iOS8: Custom Views

by Louis Tur

In the last post, I took a look at how to share data between a share extension and its container app. I'll be improving that existing extension by replacing its default SLComposeServiceViewController with a UIViewController and adding in a custom view. To give a sense of where we started, to where we are going, this is our roadmap:


Changing our Inheritence

As previously mentioned, an out-of-the-box implementation of a share extension gives you (among other things) a template of a SLComposeServiceViewController, which includes some default methods that you need to override (ie. didSelectPost, isContentValid, and configurationItems)

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

And in order to have a fully customized view, you'll need to change the class's inheritence to that of UIViewController, its direct super class, and delete all of the SLComposeServiceViewController-specific methods.

You may be thinking: "Wait, isn't there a way to modify the existing view so that you don't have to completely re-code basic functionallity?" And the answer to that is no. You can access, for example, the view that the SLComposeServiceViewController presents.. but it is read-only. And on top of that, the view itself has a nested structure of CALayers that's just a pain to go through and draw out.

What about just drawing your custom view on top of the existing one? Yea, that might work, but the issue here is that the Apple guidelines require that extensions take much less than one second to load, and that its memory footprint minimal (I received low memory warnings at about 5MB when I accidentally entered an infinite loop by overriding loadView of SLComposeServiceViewController.. otherwise my extension hovers at about 3MB).

Design your app extension to launch quickly, aiming for well under one second. An extension that launches too slowly is terminated by the system.

On top of that, an extension doesn't own the main run loop and iOS "aggressively" terminates extensions that are taking too long or block the main loop.

Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app.

... the system may aggressively terminate extensions because users want to return to their main goal in the host app...

...Your app extension doesn’t own the main run loop, so it’s crucial that you follow the established rules for good behavior in main run loops.

So, performing unnecessary UI instatiating and drawing might result in your extension just not running at all. That is, if Apple allows for such a design to even get through its approval process.


Other tidbits

The rest of the customization is exactly how you would create and present a custom view in any other context. The share extension template includes a storyboard that you can use to design the flow of your extension (if you plan on having multiple views), but you can also just have a single view represented by a nib, as I did.

If you elect to use a nib, there's just a bit of configuration that needs to be done in the extension's project settings.

  1. Remove the reference to MainInterface.storyboard in your extension's General tab
  2. Add a new key to the extension's plist called NSExtensionPrincipalClass and set it to the name of your nib's class.

Note that this key is a direct sibling of the NSExtension key that already exists in the plist. Placing it anywhere else will result in Xcode being unable to run your extension in the simulator or other device. (But fortunately you get an error that says something to the nature of "missing NSExtensionPrincipalClass")

In my implementation of the nib, I followed very closely the encapsulation guide detailed in an eppz blog post. The post itself is phenominal for upping your nib game, and the final product may be a little limiting for how you can later make changes from outside of the nib's implementation file, but you also get to call/setup your view with just this:

-(void)presentShareView{
    [ShareView presentInViewController:self];
}

The code takes care of delegation of the class and ownership of the nib. You just have to implement protocol methods defined by your nib class.


Next Up: Data Extraction

The next topic is definitely a big one, and may be split up a bit to cover NSExtensionContext and then the more intricate parsing of the HTML elements of a host app's page.

Though be sure to check out the first entry in this topic if you're interested Extensions in iOS8: Shared Storage and code will soon follow.

/>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