How to use UIActivityViewController to share stories to Instagram from your iOS app

Roman
5 min readDec 24, 2022

--

Photo by Brett Jordan on Unsplash

On my last project, I encountered an unexpected issue while trying to implement a sharing feature. While Instagram added its button to the iOS share sheet, this button only displayed when you shared a single image. In my case, we wanted to provide both a sticker and a background for an Instagram story, and we wanted to use the system’s sharing dialog for that. After some research and experimentation, a solution was found, and I am now sharing it with you.

TL;DR: At the end, you will find a link to the full solution gist.

So why do we even know that it is possible to provide both a sticker and a background to Instagram to create a story? Because we can still read the official documentation sometimes.

iOS implementations use a custom URL scheme to launch the Instagram app and pass it content. In general, your sharing flow should:

1. Check that your app can resolve Instagram’s custom URL scheme.

2. Assign the content that you want to share to the pasteboard.

3. Resolve the custom URL scheme if your app is able to.

As you can see, we can use a URL scheme to create a story and provide the attachments we want with it.

But how is it possible to use the URL scheme from the system’s share sheet? The concept behind my solution is to add a custom UIActivity to the UIActivityViewController.

At the time of publication, the Meta example code was written in Objective-C, but we will use Swift here.

The documentation also mentions that we should provide the application ID as a URL parameter, but it works fine without it, so we can skip developer registration for this one.

First of all, we need to update our info.plist file. We should add instagram-stories to the LSApplicationQueriesSchemes key:

After that, we need to create a new subclass of UIActivity:

class InstagramActivity: UIActivity {

enum Key: String {
case sticker, background

/// Instagram story properties keys
var instagramKey: String {
switch self {
case .sticker: return "com.instagram.sharedSticker.stickerImage"
case .background: return "com.instagram.sharedSticker.backgroundImage"
}
}
}

// MARK: - UIActivity properties
override class var activityCategory: UIActivity.Category { return .share }
override var activityType: UIActivity.ActivityType? { return UIActivity.ActivityType("postToInstagram") }
// button title in a share sheet
override var activityTitle: String? { return "Instagram P" }
// button icon in a share sheet
override var activityImage: UIImage? { UIImage(named: "inst_logo") }

// MARK: - Properties
private var sticker: UIImage!
private var background: UIImage!

...

}

Some basic stuff here:

  • Keys that we will use to provide data to our activity, along with their Instagram titles.
  • A bunch of overridden methods. Take note of the activityTitle and activityImage properties — these two are the title and icon of your action in the share sheet.
  • sticker and background properties for storing the data that we will provide to Instagram. I will explain the use of ! later.

Before we continue with our implementation, add this constant at the top of the file:

private let InstagramAppURL = URL(string: "instagram-stories://share")!

Next, let’s prepare a function in this class that can extract our images from the array of items provided to the UIActivityViewController:

// MARK: - Helper
private func extractImages(from activityItems: [Any]) -> (sticker: UIImage, background: UIImage)? {
guard let activityItems = activityItems.first(where: { $0 is [Key: UIImage] }) as? [Key: UIImage] else {
return nil
}

guard let sticker = activityItems[.sticker],
let background = activityItems[.background]
else { return nil }

return (sticker: sticker, background: background)
}

This function returns a tuple that contains a sticker image and a background image.

First, we search for a dictionary of type [Key: UIImage]. This is how we will provide our images. Next, we check that both sticker and background images were provided. Finally, we return a tuple with those images. We will return nil if there is no described dictionary or images.

To make our activity work properly, we need to override several UIActivity functions:

// MARK: - UIActivity
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
guard let _ = extractImages(from: activityItems) else { return false }
return UIApplication.shared.canOpenURL(InstagramAppURL)
}

With this function, iOS will check if the share sheet should even show our action. So we check if there are needed items in the correct format with our helper function. We also check if we can handle the Instagram URL. The answer will be true if the Instagram app is on the device and the info.plist file is correctly updated as described earlier.

override func prepare(withActivityItems activityItems: [Any]) {
let images = extractImages(from: activityItems)!
sticker = images.sticker
background = images.background
}

Here, we save our items. We force unwrap the return value of the extractImages(from:) function because we know that these values were already checked in the previous function, and we assume that it is safe.

override func perform() {
let pasteboardItems = [[
Key.background.instagramKey: background!,
Key.sticker.instagramKey: sticker!
]]

let pasteboardOptions = [UIPasteboard.OptionsKey.expirationDate: Date(timeIntervalSinceNow: 60*5)]

UIPasteboard.general.setItems(pasteboardItems, options: pasteboardOptions)

UIApplication.shared.open(InstagramAppURL)
}

The last function wraps the actual action logic. We prepare the data as described in the Meta documentation and open the Instagram stories URL.

At this point, our activity is ready. Let’s see how we can use it. For example, we can create a function like this in our view controller:

@IBAction func shareInstagram() {
let sticker = UIImage(named: "sticker")!
let background = UIImage(named: "background")!

let activityViewController = UIActivityViewController(
activityItems: [[
InstagramActivity.Key.sticker: sticker,
InstagramActivity.Key.background: background
]],
applicationActivities: [
InstagramActivity()
]
)

present(activityViewController, animated: true, completion: nil)
}

We just:

  1. Get images from assets
  2. Initialize the UIActivityViewController with:
    * An array of activityItems. It contains only one item — a dictionary with two images under the provided keys.
    * An array of applicationActivities. It only contains our new InstagramActivity.
  3. Present the activityViewController on screen.

Now we can bind a button to this new function or call it manually. And if you followed this tutorial correctly, after you call the shareInstagram() function, you will see the share sheet with our new sharing action. Tapping on that action will open Instagram with the opened stories editor configured with our sticker and background images.

That’s all, folks! You can find the full gist here. Feel free to ask questions and contact me on Twitter. Good luck with your coding!

--

--