App Extension with CocoaPods, plus Core Data

Prologue

AquarHEAD Lou
Eques a Issë
5 min readSep 9, 2015

--

As we’re getting closer and closer to finish our first App under the name of Ela Workshop, I’ve been stucked on the GitHub Issue of refactoring export and import data to use standard ways proposed by recent iOS releases.

We started by looking at UIActivityViewController and UIActivity, after checking multiple resources I finally realized it’s only useful for exporting the data. And in our case which we just export data as .csv or .json file, you can do it easily with just UIActivityViewController. It’s very straight and simple.

The tricky part is how to enable users to import data from (hopefully) any other app, like Dropbox.app or Mail.app, it turns out we should really use App Extension, so begins our exploration of making an App Extension with CocoaPods, plus using Core Data for data storage.

App Extension

To add an App Extension, open File > New > Target, then choose Action Extension under iOS > Application Extension category. You’ll need to give it a name, choose the language and whether you need an UI.

Xcode defaults to let the extension visible in every app for ease of debugging, but before submission you need to modify what things your extension can accept in the Info.plist file (the one inside the extension folder). If you want to allow only a single JSON file as input, change the NSExtensionAttributes dict to something like this:

If you want other behaviours, you should check Apple documents or maybe google a bit, if you don’t really care what UTI the file is, you can simply use some key-value pairs in NSExtensionActivationRule, it’s also documented in the above link.

Another common thing is if you want to use some code in the main app inside your App Extension, you need to tell Xcode to compile specific source files into the extension as well. I found this answer simple enough, but be ware that if the source file you’re including also #import other code or even things installed by CocoaPods, it can become problematic. We’ll look into that in the next section.

Other parts of an App Extension is very much like how you write code normally in the main app, you can make UI in the storyboard, connect things around, etc.. The example code created by Xcode is also very useful and straightforward.

For extension that deals with files, and if you expect people to grab their file from Dropbox, be ware (at least I suspect) that Dropbox has 2 layers of UIActivityViewController: the first one will simply pass your extension an URL, and you need to overwrite the ?dl=0 to ?dl=1 to allow your extension able to download the actual content instead of a silly web page preview (you also need to specify allow URL in the extension attrbutes plist)…

The second one, which can be opened by choosing “Open in…” in the first popup, will trully give your extension a file, so actually you can just support file types not URL, but probably you should guide your user to find the “Open in…” to minimize confusion.

+CocoaPods

It’s not very hard to use code from your main app in App Extension, but for 3rd party libraries installed via CocoaPods, it can become a nightmare. If you compile your code immediately after adding the extension and you’re using some full-featured pods (like AFNetworking or SVProgressHUD), you’ll most likely seeing many errors. The solution is to add some #define to tell compiler to ignore those codes that are using features not available in an App Extension.

You’ll need to check each failed pod for specific flag to define in the compiler settings, and generally use something like this:

Also you may encounter other issues if you use a same pod in both main app and extension, like discussed in the SVProgressHUD issue, but a good thing is most of these issues have already been solved by others, just follow their example and you should be fine (although sometimes it looks ugly to use so many hacks).

If you previously just put all pods defination outside of any target block, you’ll probably need to manually set the base configuration for the project. CocoaPods should also warn you at the end of pod install, just follow this thread and config the project as CocoaPods tell you.

+Core Data

It adds another layer of complexity when Core Data kicks in.

First to be able to call methods in Core Data objects, you just do the same as what you did for other source files in the main app, then also add your extension to Target Membership of the .xcdatamodeld file.

But these are not enough if you want the main app and app extension to use a shared database, because Apple provides seperate container for even your own app and extension. If you’re setting up the Core Data stack by default, it will stay in either the main app’s container or the extension’s, and as you would expect they’re not the same. Fortunately, you can add your app and App Extension to an App Group, then you can use a shared container between app and extension. By setting up Core Data in that container you can share database usage.

If you use MagicalRecord, just use the following code in both your main app and your App Extension to setup Core Data before interacting with it.

Reference

This concludes our first (somewhat) technical writings as Ela Workshop.

Without the following useful readings and combined force with @xhacker I won’t be able to figure it out. So check these if you want to know more and be sure to follow us for future news/stories while we do all kinds of interesting things :)

--

--