# UIActivityViewController

By [Mattt](https://nshipster.com/authors/mattt).
Published 2014-04-21; updated 2018-12-05.
Canonical URL: <https://nshipster.com/uiactivityviewcontroller>

On iOS, `UIActivityViewController` provides a unified interface for users to share and perform actions on strings, images, URLs, and other items within an app.

You create a `UIActivityViewController` by passing in the items you want to share and any custom activities you want to support (we’ll show how to do that later on). You then present that view controller as you would any other modal or popover.

```swift
let string = "Hello, world!"
let url = URL(string: "https://nshipster.com")!
let image = UIImage(named: "mustache.jpg")
let pdf = Bundle.main.url(forResource: "Q4 Projections",
                            withExtension: "pdf")

let activityViewController =
    UIActivityViewController(activityItems: [string, url, image, pdf],
                             applicationActivities: nil)

present(activityViewController, animated: true) {
    <#…#>
}
```

When you run this code the following is presented on the screen:

![UIActivityViewController](https://nshipster.com/assets/uiactivityviewcontroller-e8c00397981b9c93.png)

By default, `UIActivityViewController` shows all the activities available for the items provided, but you can exclude certain activity types via the `excludedActivityTypes` property.

```swift
activityViewController.excludedActivityTypes = [.postToFacebook]
```

Activity types are divided up into “action” and “share” types:

- **Action** (`UIActivityCategoryAction`) activity items take an action on selected content, such as copying text to the pasteboard or printing an image.
- **Share** (`UIActivityCategoryShare`) activity items share the selected content, such as composing a message containing a URL or posting an image to Twitter.

Each activity type supports certain kinds of items. For example, you can post a String, URL, and / or image to Twitter, but you can’t assign a string to be the photo for a contact.

The following tables show the available activity types for each category and their supported items:

### UIActivityCategoryAction

| | | ![](https://nshipster.com/assets/uiactivity-icon-string-eba38b6716ea7bf7.svg) | ![](https://nshipster.com/assets/uiactivity-icon-url-773060240285f30c.svg) | ![](https://nshipster.com/assets/uiactivity-icon-image-f85404a770efc970.svg) | ![](https://nshipster.com/assets/uiactivity-icon-file-1cd0ecb77a1285be.svg) |
| - | - | - | - | - | - |
| | | String | URL | Image | Files |
| ![](https://nshipster.com/assets/uiactivity-airDrop-ae6d3c370d8304f3.png) | `airDrop` | ✓ | ✓ | ✓ | ✓ |
| ![](https://nshipster.com/assets/uiactivity-addToReadingList-e032bf778dce9d9c.png) | `addToReadingList` | | ✓ | | |
| ![](https://nshipster.com/assets/uiactivity-assignToContact-3f5bef0bcf8f8832.png) | `assignToContact` | | | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-copyToPasteboard-050df00023bc0408.png) | `copyToPasteboard` | ✓ | ✓ | ✓ | ✓ |
| ![](https://nshipster.com/assets/uiactivity-print-ed449059c5f93f5b.png) | `print` | | | ✓ | ✓ |
| ![](https://nshipster.com/assets/uiactivity-saveToCameraRoll-fed1668a76f8c7c3.png) | `saveToCameraRoll` | | ✓ | ✓ | |

### UIActivityCategoryShare

| | | ![](https://nshipster.com/assets/uiactivity-icon-string-eba38b6716ea7bf7.svg) | ![](https://nshipster.com/assets/uiactivity-icon-url-773060240285f30c.svg) | ![](https://nshipster.com/assets/uiactivity-icon-image-f85404a770efc970.svg) | ![](https://nshipster.com/assets/uiactivity-icon-file-1cd0ecb77a1285be.svg) |
| - | - | - | - | - | - |
| | | String | URL | Image | Files |
| ![](https://nshipster.com/assets/uiactivity-mail-9aeb25bd2511d6b0.png) | `mail` | ✓ | ✓ | ✓ | ✓ |
| ![](https://nshipster.com/assets/uiactivity-message-540a720d5e03eb0e.png) | `message` | ✓ | ✓ | ✓ | ✓ |
| ![](https://nshipster.com/assets/uiactivity-postToFacebook-4160cd80505b6925.png) | `postToFacebook` | ✓ | ✓ | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-postToFlickr-4b321a45b1c6d18d.png) | `postToFlickr` | | ✓ | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-postToTencentWeibo-5e8db37cbad8f934.png) | `postToTencentWeibo` | ✓ | ✓ | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-postToTwitter-2b2cbcb509e035d0.png) | `postToTwitter` | ✓ | ✓ | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-postToVimeo-bd1ae391c5dab626.png) | `postToVimeo` | | ✓ | ✓ | |
| ![](https://nshipster.com/assets/uiactivity-postToWeibo-c3127579f87f0351.png) | `postToWeibo` | ✓ | ✓ | ✓ | |

> `UIActivityViewController` allows users to choose how they share content. However, as a developer, you can access this functionality directly. Here are the corresponding APIs for each of the system-provided activity types:
>
> | Activity Type | Corresponding API |
> | - | - |
> | `addToReadingList` | [`SSReadingList`](https://developer.apple.com/documentation/safariservices/ssreadinglist/1621226-additem) |
> | `assignToContact` | [`CNContact`](https://developer.apple.com/documentation/contacts/cncontact) |
> | `copyToPasteboard` | [`UIPasteboard`](https://developer.apple.com/documentation/uikit/uipasteboard/1829417-setitems) |
> | `print` | [`UIPrintInteractionController`](https://developer.apple.com/documentation/uikit/uiprintinteractioncontroller/1618174-printtoprinter) |
> | `saveToCameraRoll` | [`UIImageWriteToSavedPhotosAlbum`](https://developer.apple.com/documentation/uikit/1619125-uiimagewritetosavedphotosalbum) |
> | `mail` | [`MFMailComposeViewController`](https://developer.apple.com/documentation/messageui/mfmailcomposeviewcontroller) |
> | `message` | [`MFMessageComposeViewController`](https://developer.apple.com/documentation/messageui/mfmessagecomposeviewcontroller) |
> | `postToFacebook` `postToFlickr` `postToTencentWeibo` `postToTwitter` `postToVimeo` `postToWeibo` | [`SLComposeViewController`](https://developer.apple.com/documentation/social/slcomposeviewcontroller) |

## Creating a Custom UIActivity

In addition to the system-provided activities, you can create your own activities.

As an example, let’s create a custom activity that takes an image and applies a mustache to it via a web application.

| | |
| - | - |
| ![Jony Ive Before](https://nshipster.com/assets/jony-ive-unstache-e896d2b6a73b88b4.png) | ![Jony Ive After](https://nshipster.com/assets/jony-ive-mustache-8b1d770ac3808a95.png) |
| Before | After |

### Defining a Custom Activity Type

First, define a new activity type constant in an extension to `UIActivity.ActivityType`, initialized with a [reverse-DNS identifier](https://en.wikipedia.org/wiki/Reverse_domain_name_notation).

```swift
extension UIActivity.ActivityType {
    static let mustachify =
        UIActivity.ActivityType("com.nshipster.mustachify")
}
```

### Creating a UIActivity Subclass

Next, create a subclass of `UIActivity` and override the default implementations of the `activityCategory` type property and `activityType`, `activityTitle`, and `activityImage` instance properties.

```swift
class MustachifyActivity: UIActivity {
    override class var activityCategory: UIActivity.Category {
        return .action
    }

    override var activityType: UIActivity.ActivityType? {
        return .mustachify
    }

    override var activityTitle: String? {
        return NSLocalizedString("Mustachify", comment: "activity title")
    }

    override var activityImage: UIImage? {
        return UIImage(named: "mustachify-icon")
    }

    <#…#>
}
```

### Determining Which Items are Actionable

Activities are responsible for determining whether they can act on a given array of items by overriding the `canPerform(withActivityItems:)` method.

Our custom activity can work if any of the items is an image, which we identify with some fancy pattern matching on a for-in loop:

```swift
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
    for case is UIImage in activityItems {
        return true
    }

    return false
}
```

### Preparing for Action

Once an activity has determined that it can work with the specified items, it uses the `prepare(withActivityItems:)` method to get ready to perform the activity.

In the case of our custom activity, we take the PNG representation of the first image in the array of items and store that in an instance variable:

```swift
var sourceImageData: Data?

override func prepare(withActivityItems activityItems: [Any]) {
    for case let image as UIImage in activityItems {
        self.sourceImageData = image.pngData()
        return
    }
}
```

### Performing the Activity

The `perform()` method is usually the most important part of your activity. Because processing can take some time, this is an asynchronous method. However, for lack of a completion handler, you signal that work is done by calling the `activityDidFinish(_:)` method.

But if an activity provides a view controller (as ours will, to show the result), UIKit presents that view controller instead of calling `perform()`, so the work has to happen there.

Our custom activity delegates the mustachification process to a web app using a data task sent from the shared `URLSession`. The `mustachify(completion:)` method calls its completion handler on the main queue with the resulting image, or with `nil` if an error occurred in the request or we can’t create an image from the provided data.

```swift
func mustachify(completion: @escaping (UIImage?) -> Void) {
    let url = URL(string: "https://mustachify.app/")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = self.sourceImageData

    URLSession.shared.dataTask(with: request) { (data, _, error) in
        var image: UIImage?
        if error == nil, let data = data {
            image = UIImage(data: data)
        }

        DispatchQueue.main.async {
            completion(image)
        }
    }.resume()
}
```

### Showing the Results

The final step is to provide a view controller to be presented with the result of our activity.

We override the `activityViewController` property to return a navigation controller containing a simple view controller that displays the image:

```swift
override var activityViewController: UIViewController? {
    let viewController = MustachifyViewController(activity: self)
    return UINavigationController(rootViewController: viewController)
}
```

When its view loads, the view controller asks the activity to mustachify the image and shows the result in an image view. The “Done” button stays disabled until the image arrives. When the user taps it, it calls `activityDidFinish(_:)` with `true` to indicate that the activity finished successfully, and UIKit dismisses it. If the image couldn’t be mustachified, or if the user taps “Cancel”, it calls `activityDidFinish(_:)` with `false` to indicate failure.

```swift
class MustachifyViewController: UIViewController {
    let activity: MustachifyActivity
    let imageView = UIImageView()

    init(activity: MustachifyActivity) {
        self.activity = activity
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = .white
        self.imageView.frame = self.view.bounds
        self.imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.imageView.contentMode = .scaleAspectFit
        self.view.addSubview(self.imageView)

        self.navigationItem.rightBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .done,
                            target: self,
                            action: #selector(done))
        self.navigationItem.rightBarButtonItem?.isEnabled = false
        self.navigationItem.leftBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .cancel,
                            target: self,
                            action: #selector(cancel))

        self.activity.mustachify { [weak self] image in
            guard let self = self else { return }
            if let image = image {
                self.imageView.image = image
                self.navigationItem.rightBarButtonItem?.isEnabled = true
            } else {
                self.activity.activityDidFinish(false)
            }
        }
    }

    @objc func done() {
        self.activity.activityDidFinish(true)
    }

    @objc func cancel() {
        self.activity.activityDidFinish(false)
    }
}
```

### Providing a Custom Activity to Users

We can use our brand new mustache activity by passing it to the `applicationActivities` parameter in the `UIActivityViewController` initializer:

```swift
let activityViewController =
    UIActivityViewController(activityItems: [image],
                             applicationActivities: [MustachifyActivity()])

present(activityViewController, animated: true) {
    <#…#>
}
```

![](https://nshipster.com/assets/uiactivityviewcontroller-custom-action-ac216a40a5a58294.png)

---

There is a strong argument to be made that the long-term viability of iOS as a platform depends on sharing mechanisms like `UIActivityViewController`.

As the saying goes, _“Information wants to be free.”_ Anything that stands in the way of federation is doomed to fail.
