A lot of developers want to be able to share their app data via email, Messages, or AirDrop. Sharing is a convenient way for users to send data to each other or between devices – and it may even net you some new customers!
Luckily, since iOS6 Apple has provided the handy but much overlooked UIActivityViewController
class which provides a clean interface for sharing and manipulating data inside your app. You’re going to learn everything you need to know in this UIActivityViewController tutorial!
To setup sharing inside your app you just have to configure a few keys inside your applications Info.plist file and handle a few system callbacks inside your applications AppDelegate. Once setup, iOS can open your app with the URL pointing to the data to import or export.
Ready for some beer sharing fun? Read on.
Getting Started
Start by downloading the starter project for this tutorial, which is a modified version of the Beer Tracker app used in a previous tutorial. Build and run (Product \ Run or ⌘R) the project in Xcode; you’ll see the following:
Well, that’s no good, there are no beers to share. Lets get started so you can start sharing wonderful beers with everyone.
UTIs and your Plist
The first thing you need to do is set up your Info.plist to let iOS know your app can handle Beer Tracker Documents. To do this you need to register your app as being able to handle certain Uniform Type Identifiers, or UTIs, exporting any UTIs that are not already known by the system.
In summary, UTIs are unique identifiers that represent documents. There are UTIs already built-in to iOS for handling common document types such as public.jpeg or public.html.
You’re going to register your app to handle documents with the com.raywenderlich.BeerTracker.btkr UTI representing the description of a beer. You’ll tell iOS information about the UTI such as what file name extension it uses, what mime-type it’s encoded as when sharing and finally the file’s icon.
So let’s see it in action! Open Info.plist, and add the following entries under the Information Property List
key:
You can read up on what each of these value’s mean in Apple’s UTI guide, but here are the important things to note:
- The
Document types
entry defines what UTIs your app supports – in your case, the com.raywenderlich.BeerTracker.btkr UTI, as an Owner/Editor. - The
Document types
is also where you set the names of the icons iOS should use when displaying your file type. You’ll need to make sure you have an icon for each of the sizes listed in the plist. - The
Exported Type UTIs
entry gives some information about com.raywenderlich.BeerTracker.btkr, since it isn’t a public UTI. Here you define files ending in .btkr, or files that have a mime type of application/beertracker can be handled by your app.
Believe it or not, by setting these keys, you have informed iOS to start sending your app files that end with the .btkr extension. You can test this out by emailing yourself a copy of this sample beer file. Please make sure you unzip the file before emailing it to yourself otherwise both the file extension UTI and mime type will be wrong.
You can tap on the attachment and it will prompt you to open the beer in the Beer Tracker app. Selecting Beer Tracker will open the app, however it won’t load the data from the sample file because you haven’t implemented the code for that yet.
Now that you are a UTI wizard, lets sprinkle some magic.
UIActivityViewController
displayed after tapping the attached file in your email, you may need to edit the order of supported applications by scrolling to the end of the list, selecting More, and movings “Copy to Beer Tracker” to the top of the list.Importing App Data
Before you can handle opening data from the file, you’ll need some code that can work with the file passed. Open Beer.swift, and replace the importDataFromURL(_:)
method with the following:
static func importDataFromURL(url: NSURL) { // 1 guard let dictionary = NSDictionary(contentsOfURL: url), beerInfo = dictionary as? [String: AnyObject], name = beerInfo[Keys.Name.rawValue] as? String, rating = beerInfo[Keys.Rating.rawValue] as? NSNumber else { return } // 2 let beer = Beer(name: name, note: beerInfo[Keys.Note.rawValue] as? String, rating: rating) // 3 if let base64 = beerInfo[Keys.ImagePath.rawValue] as? String, imageData = NSData(base64EncodedString: base64, options: .IgnoreUnknownCharacters), image = UIImage(data: imageData) { beer.saveImage(image) } // 4 BeerManager.sharedInstance.beers.append(beer) BeerManager.sharedInstance.saveBeers() // 5 do { try NSFileManager.defaultManager().removeItemAtURL(url) } catch { print("Failed to remove item from Inbox") } } |
Here’s a step-by-step explanation of the above code:
- Verify the app can read the contents of the URL provided to it. Once verified make sure the data is of a type the application expects and has the minimum data set required to import a beer.
- Create a new
Beer
object with data from the URL. - If image information exists in the beer data, process the image and persist to disk.
- Add the new beer to your app’s data
- Finally, delete the document that was saved to your app’s sandbox when iOS opened it.
Next, when an external app wants to send your app a file, it does so via the application(_:openURL:options:)
method. Open AppDelegate.swift, and add the following code underneath the application(_:didFinishLaunchingWithOptions:)
method:
// MARK: - Handle File Sharing func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool { // 1 guard url.pathExtension == "btkr" else { return false } // 2 Beer.importDataFromURL(url) // 3 guard let navigationController = window?.rootViewController as? UINavigationController, beerTableViewController = navigationController.viewControllers.first as? BeersTableViewController else { return true } // 4 beerTableViewController.tableView.reloadData() return true } |
Here’s a step-by-step explanation of the above:
- Verify the URL’s extension is btkr since your app only supports files with that extension.
- Use the
static
method on the Beer object you added above to import the data into your app. - Verify the root view controller is an instance of a
UINavigationController
and that its first view controller is an instance ofBeersTableViewController
. - Reload
BeersTableViewController
‘s table view to show the newly imported beer, then return true to inform iOS that your app successfully processed the provided beer information.
Build and run your app. If all works well you should be able to open the email attachment and see the beer imported into your app as shown below.
Exporting App Data
So far in this UIActivityViewController tutorial you’ve added functionality to your app that handles importing data from other apps, however what if you wish to share your app’s data? You’re in luck here, as Apple has made exporting data almost as nice as a lime infused cerveza on a hot beach – maybe.
Your app will need code to handle brewing exporting your favorite beers. Open Beer.swift and replace the existing exportToFileURL()
definition with the following:
func exportToFileURL() -> NSURL? { // 1 var contents: [String: AnyObject] = [Keys.Name.rawValue: name, Keys.Rating.rawValue: rating] // 2 if let image = beerImage() { let data = UIImageJPEGRepresentation(image, 1) contents[Keys.ImagePath.rawValue] = data?.base64EncodedStringWithOptions(.Encoding64CharacterLineLength) } // 3 if let note = note { contents[Keys.Note.rawValue] = note } // 4 guard let path = NSFileManager.defaultManager() .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first else { return nil } // 5 let saveFileURL = path.URLByAppendingPathComponent("/\(name).btkr") (contents as NSDictionary).writeToURL(saveFileURL, atomically: true) return saveFileURL } |
Here’s a step-by-step explanation of the above code:
- Create a
Dictionary
to hold your basic beer information. - If you have an image, encode it to a base64 string to save in the dictionary. This makes it very easy to share your photo and data all in the same file.
- If you have notes, save those as well.
- Verify your app can access its Documents directory without error.
- Finally, persist the dictionary data to your Documents directory, and return the URL to the newly created file.
Now that you can export Beer data to a file, you’re going to need an easy way to share it. Open BeerDetailViewController.swift, and replace the implementation of the share(_:)
method with the following:
@IBAction func share(sender: AnyObject) { guard let detailBeer = detailBeer, url = detailBeer.exportToFileURL() else { return } let activityViewController = UIActivityViewController( activityItems: ["Check out this beer I liked using Beer Tracker.", url], applicationActivities: nil) presentViewController(activityViewController, animated: true, completion: nil) } |
Here you’ve added a check to verify you have a detail beer, and can retrieve the URL from the exportToFileURL()
method. Next, you create an instance of a UIActivityViewController
passing in a message to be used, if possible and the URL to your app’s data file. UIActivityViewController
will do all of the heavy lifting for you. Since you defined all of the required information in your Info.plist file, it’ll know just what to do with it. Finally you present the UIActivityViewController
to the user.
UIActivityViewController
has been around since iOS6, however it is a very useful and sometimes under appreciated class. The message you pass to UIActivityViewController
will be used by applications such as Mail and Messages, adding this to the body of your message.Build and run your app, open a beer, and try to share. You’ll notice that you see a few options available to you as shown below:
Perhaps one the best options is the ability to AirDrop. If you have two devices – or better yet, a friend with the Beer Tracker app installed – you can test AirDropping beers!
Where To Go From Here?
You can download the finished project from this UIActivityViewController tutorial here.
Now that you know how iOS imports and exports app data, you’re ready to take this knowledge and start sharing your favorite beers or any other data of your choice.
In the meantime, if you have any questions or comments about this tutorial or Sharing Data in general, please join the forum discussion below!
The post UIActivityViewController Tutorial: Sharing Data appeared first on Ray Wenderlich.