December 24, 2019

Share text or images with UIActivityViewController in Swift

Share text or images with UIActivityViewController in Swift

UIActivityViewController makes sharing easy and it allows one to share many different types content including, text, images, URL's, Audio and more.

In this tutorial we will learn how to share images, text and a URL. All of these use the same code, but we will also learn how to share on iPhone and iPad because the two platform work differently. Luckily it only takes one line to get UIActivityViewController to on iPad.

We are going to add a method called, share, which will take three arguments, items, excludedActivityTypes and ipad.

Let's create the method, add the following to your code:

private func share(items: [Any],
                   excludedActivityTypes: [UIActivity.ActivityType]? = nil,
                   ipad: (forIpad: Bool, view: UIView?) = (false, nil)) {
    // Initialise the UIActivityViewController
    // Check if this needs to display on iPad
    // Check if there are any excluded activity types, and if there are, set them
    // present the activityViewController
}

This method does nothing at the moment but I have added comments in as placeholders for our functionality.

If you take a look at the third argument, the one for ipad, you will see that this is a tuple. The reason that I went with a tuple is because I did not want two arguments in order to handle if we are catering for an ipad.

Let's start implementing this method by replacing the first argument with the following:

let activityViewController = UIActivityViewController(activityItems: items,
                                                      applicationActivities: nil)

This will initialise our UIActivityViewController with the items that we want to share.

Before we carry on, let me explain what items are. Items are all the things that we would like to share. So if we wanted to share text, the items in this example will be an array with text in it. If we wanted to share an image, the items array would contain an image/s. But, because we made our items an array of type Any, we can share multiple different types of content. If we wanted to share text and an image, we would simply put text and an image in this array, but we will see how to do this later on.

The next part is to check if we need to display this on iPad. Replace the next comment, Check if this needs to display on iPad, with the following code:

if ipad.forIpad {
    activityViewController.popoverPresentationController?.sourceView = ipad.view
}

As mentioned previously, I used a tuple as the argument type for ipad. But this is a tuple with named elements which makes it easier to read and understand. If we do not use names, we would need to do something like this, ipad.0 or ipad.1, which isn't very readable.

This tuple has two elements, the first is forIpad which is a Bool. If it is true then we need to set the popoverPresentationController's sourceView. Luckily the tuple has the view in the second element called, view, which we assign to sourceView.

Now, we need to set the excludedActivityTypes. Replace the third comment with the following code:

if let excludedActivityTypes = excludedActivityTypes {
    activityViewController.excludedActivityTypes = excludedActivityTypes
}

We are checking to see if there are any excludedActivityTypes, if there are then we will assign them to activityViewController.excludedActivityTypes.

Lastly we need to present the activityViewController. This is a simple one liner, replace the last comment with the following code:

self.present(activityViewController, animated: true)

It would probably be better to return the activityViewController instead of presenting it like this, update the last line that we added, and replace it with this:

return activityViewController

We will now have an error because we need to add a return type to our method signature. Replace your method signature with the following:

private func share(items: [Any],
                   excludedActivityTypes: [UIActivity.ActivityType]? = nil, 
                   ipad: (forIpad: Bool, view: UIView?) = (false, nil)) -> UIActivityViewController

Before we carry on, this is what the final version of the method looks like:

private func share(items: [Any],
                   excludedActivityTypes: [UIActivity.ActivityType]? = nil,
                   ipad: (forIpad: Bool, view: UIView?) = (false, nil)) -> UIActivityViewController {
    let activityViewController = UIActivityViewController(activityItems: items,
                                                          applicationActivities: nil)
    if ipad.forIpad {
        activityViewController.popoverPresentationController?.sourceView = ipad.view
    }
    
    if let excludedActivityTypes = excludedActivityTypes {
        activityViewController.excludedActivityTypes = excludedActivityTypes
    }
    
    return activityViewController
}

If we build and run the app nothing will happen. I am going to add the following code to my viewDidAppear:

let activityViewController = self.share(items: ["text test",
                                        UIImage(imageLiteralResourceName: "testImage"),
                                        URL(string: "https://google.com")!])
self.present(activityViewController,
             animated: true)

In this example we are only passing through the items that we want to share and as you can see, we are sharing text, an image and a url. They all go into the same array.

This is a list of the excludedActivityTypes that you can use:

.postToFacebook
.postToTwitter
.postToWeibo
.message
.mail
.print
.copyToPasteboard
.assignToContact
.saveToCameraRoll
.addToReadingList
.postToFlickr
.postToVimeo
.postToTencentWeibo
.airDrop
.openInIBooks
.markupAsPDF

To use them, just pass an array of them as an argument, and example would look like this:

self.share(items: ["text test"], 
           excludedActivityTypes: [.postToFacebook, .postToTwitter])

If you want to test this on the iPad this is an example:

self.share(items: ["text test"], 
           ipad: (true, self.view))

If you build and run your app now you should see the following:

There is one last thing that we need to do. As you can see in the above image there is an option to Save Image. If you tap on this option the app will crash, so we need to update our info.plist.

Add the following key to your info.plist:

NSPhotoLibraryAddUsageDescription

To add this open your info.plist. You will now need to add a new key to your plist, to do this click on the little plus sign to the right of the heading, Information Property List, it will look like this:

We can now add our key, NSPhotoLibraryAddUsageDescription:

Once you press enter the key will change to different text, don't worry about this. You will see it in the next image.

Next we need to add the value of our key. This should be a descriptive text to explain what you want to do, for this example I am going to use Save Image.

As mentioned before, the key's text has changed, and we have added Save Image as our value.

You will now be able to build and run the app and tap on the Save Image option.

Conclusion

Sharing is made easy with UIActivityViewController, but I would suggest that you test this functionality thoroughly so that you are sure that the sharing behaves in the way you expect it to.

If you want the full source code you can find that here.