Building an iOS 9 Share Extension with Swift 2.0

by | Oct 30, 2015




Transcript – Building an iOS 9 Share Extension with Swift 2.0

Hi everyone! Today I want to share with you building share extensions using Swift. We’re going to write a simple app that lets us select an image in our photo album, and share it to our application.

So first what is a share extension? A share extension is a mini app we can associate with our main app, that can run from the common share sheet.

For example, when you select an image, and then select the share icon, you have a choice of applications to share the image to. The usual suspects are Facebook, Twitter, Message and so on. Our goal is to make our application available here.

So I said that this is really a mini app that we can associate with our main app.

That means the life cycle of the share app is a bit different for our main app. A share extension is started by the user selecting the icon for our app.

iOS will launch the share app. This has to happen very quickly, or iOS will shut us down. We don’t want to do anything time-consuming here.

If the user selects and posts an item, our code will run. The item can really be anything shareable. It can be a URL, image, text, or all of the above.

Once selected and processed, the app is shut down.

Let’s start by looking at the app we’re going to build.

This app allows us to select a photo from the photo album on our device. We’ll also select a color to associate with the photo so we can demonstrate selecting information outside of the defaults. The color will tell our app which page it should save the image. We then will share the image and the page with our application. We’re going to ignore the text the user types in, but you can read it and process it with the variable contentText. It’s trivial.

When we launch our app, there are two buttons. One for the red page, and one for the blue page. When we go to the red page, the image we selected for red will be displayed. The blue page will display the image we associated with blue.

Our app is composed of the following parts. We have the main view controller with two buttons.

Each segues to a view controller capable of displaying an image. The image view controllers are assigned a color red, and a color blue – just so we can tell the difference. They look in the user defaults for an image saved to either red or blue.

In our project we also have a separate target for the share extension. This is the “mini” app I talked about eariler. For this tutorial, we’re going to use Apple’s out of the box view controller for sharing.

This view controller is the SLComposeServiceViewController class. If we want to do a custom share extension view, we’d just use our own view controller. However, we’d also lose the built in functionality.

Since we want the user to select where to display an image, we have a second view controller in our share extension. This is just a table view with a list of selectable choices. This could be dynamic and read from a database, but we’ll just use red and blue.

Implementing share extensions in Swift

Alright! Let’s implement this app.

The first part is our application. I won’t go into too much detail here. There’s not too much to it. Two buttons segue. In the destination view controller code, we read the image from the user defaults, using either a red key or blue key. This is the code for the red view controller.

class RedViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    let suiteName = "group.deegeu.swift.share.extension"
    let redDefaultKey = "RedColorImage"
    
    // Simply reads the selected image from NSUserDefaults and displays the
    // image
    override func viewDidLoad() {
        super.viewDidLoad()
        if let prefs = NSUserDefaults(suiteName: suiteName) {
            if let imageData = prefs.objectForKey(redDefaultKey) as? NSData {
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.imageView.image = UIImage(data: imageData)
                })
            }
        }
    }
}

The next thing we need to do is create our share extension. With our app loaded, we go to “File | New | Target”. We create a share extension, and give it another project name. I usually add something like share-extension at the end of the project name. This creates a project with single view controller and storyboard.

Since our app extension is really a second application, it’s in it’s own sandbox. We need a way to share data from this share app to our main app. Apple allows you to do this though app groups. We need to add our app and our share extension to the same app group.

We’ll go to the project page, and select the Capabilities tab. From there we’ll open up the App Groups section and turn it on. We’ll also add a app group for these apps. It starts with groups, and then I usually use the project name. We need to do this for both the app and the share app targets.

The app group name is the name we use for the user defaults suite name. This is how we are sharing data between the applications for this example. You’d still need to add each to a common app group if you were using some other way to share data, such as a common file system or core database. When apps are part of the same app group, they can share the same user defaults.

Back in our share extension target, lets look at the ShareViewController. The view controller is a subclass of SLComposeServiceViewController. This is the default, and for most sharing tasks, it will accomplish what we need. It presents a controller with a text edit, and a place we can display a thumbnail image. It also allows us to cancel or post.

There are three methods provided for us in the view controller. configurationItems, isContentValid, and didSelectPost.

import UIKit
import Social

class ShareViewController: SLComposeServiceViewController {

    override func isContentValid() -> Bool {
        return true
    }

    override func didSelectPost() {
        self.extensionContext!.completeRequestReturningItems([], 
                completionHandler: nil)
    }

    override func configurationItems() -> [AnyObject]! {
        return []
    }

}

isContentValid is where we would check to make sure the input data is valid. It’s called first, and will disable the post button when it evaluates to false. We’ll always return true, because we’re not doing anything with the text. We’ll use another method later to force them to always pick images.

Configuration items is where we can add more things for the user to select when they are sharing. In our app they are sharing a photo, but they are also selecting which page to add the image. This is what we will use to select between red and blue.

A configuration item has a title, value, and a tapHandler. The title is the name of the configuration. We’ll call ours color. The value is the currently selected value.

When a user selects a configuration item, we’ll launch another sub view controller for this particular configuration item. The method to launch the color selection controller is called the tapHandler. The color selection view controller is a table view with the selection items.

There’s nothing fancy in the table view controller, other than the selection choices. We could make this dynamic, but for this example we’ll just hard code the choices. We’ll pass back their selected value through delegation.

When the user presses the post button, didSelectPost is called. This is where we do something with the user input. The view controller has a extensionContext where we get a list of NSExtensionItems. This is an immutable array of data sent to our extension from a parent app. For our case, the photos app is the parent app.

The extension item will have a list of attachments all typed as NSItemProvider. NSItemProvider items are lazily loaded images, videos, URLs and so on. Since we only want a photo, we’ll loop through the attachments, and look for one typed as an image. It will have the type identifier kUTTypeImage.

Once we find an image, we’ll load it using loadItemForTypeIdentifier. This has a completion handler, where we’ll read the image from the URL, and then we’ll save it to defaults. We save the actual image instead of a pointer, because the Photos app is not in the same app group as our app or share extension. The share extension is saving the image somewhere where we can read it again.

    // Called after the user selects an image from the photos
    override func didSelectPost() {
        // Make sure we have a valid extension item
        if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
            let contentType = kUTTypeImage as String
            
            // Verify the provider is valid
            if let contents = content.attachments as? [NSItemProvider] {
                // look for images
                for attachment in contents {
                    if attachment.hasItemConformingToTypeIdentifier(contentType) {
                        attachment.loadItemForTypeIdentifier(contentType, options: nil) { data, error in
                            let url = data as! NSURL
                            if (!self.selectedColorName.isEmpty) {
                                if let imageData = NSData(contentsOfURL: url) {
                                    // Save the image
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // Unblock the UI.
        self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
    }

At this point the app should run, with two slightly minor points. The first is, we can select more than one image. In fact we can share other things our code isn’t prepared to handle. The second is when we compile we’ll get a warning when we compile. It says, “Warning: Embedded binary’s NSExtensionActivationRule is TRUEPREDICATE”. Apple wouldn’t accept this in their app store. What this error is trying to say is “hey this extension is activated for everything, and you can’t do that.” We need to tell iOS when to show our share extension, and what can be selected.

Open the info.plist as source code by right clicking on it. We’re going to change the NSExtensionAttributes section.

Replace it with this XML. This tells iOS to only allow the share extension to be activated when we select a single image. This means our extension is activated only for images, and only when it’s a single image. If you select two, the extension won’t activate.

<key>NSExtensionAttributes</key>
<dict>
    <key>NSExtensionActivationRule</key>
    <dict>
       <key>NSExtensionActivationSupportsImageWithMaxCount</key>
       <integer>1</integer>
    </dict>
</dict>

Wow. That was a big tutorial for a simple thing. The source code is up on Github, so you’ll want to look over it.

If you have any questions let me know in the comments. New videos are out every week, so make sure you subscribe. This puts new videos in your YouTube feed. If I did a good job, let me know!

And with that, I’ll see you in the next tutorial!




Related Posts

Tools Used

  • Java
  • NetBeans

Media Credits

All media created and owned by DJ Spiess unless listed below.

  • No infringement intended

Get the code

The source code for “Building an iOS 9 Share Extension with Swift 2.0” can be found on Github. If you have Git installed on your system, you can clone the repository by issuing the following command:

 git clone https://github.com/deege/deegeu-swift-share-extensions.git

Go to the Support > Getting the Code page for more help.

If you find any errors in the code, feel free to let me know or issue a pull request in Git.

Don't miss another video!


New videos come out every week. Make sure you subscribe!



Comments

comments

DJ Spiess

DJ Spiess

Your personal instructor

My name is DJ Spiess and I’m a developer with a Masters degree in Computer Science working in Colorado, USA. I primarily work with Java server applications. I started programming as a kid in the 1980s, and I’ve programmed professionally since 1996. My main focus are REST APIs, large-scale data, and mobile development. The last six years I’ve worked on large National Science Foundation projects. You can read more about my development experience on my LinkedIn account.

Pin It on Pinterest

Share This