How do Share and Action extensions work, and how do they receive and return data?

19.3.1 · L3 · Interview

Predict first

A Share extension must send a 200 MB video to a server. Where should the upload happen?

// A: inside the extension, while the share sheet is open
// B: in the app or a background URLSession using the shared container, after the extension saves the file
// C: nowhere, extensions cannot share files
  • A
  • B
  • C

Mental model. A Share extension is a mail slot in your shop’s door that anyone on the street can post something through. It must accept the parcel quickly and put it in a tray (the shared container), because the postman does not wait while you open it.

A Share extension (com.apple.share-services) lets users send content from another app into yours through the share sheet: a link in Safari into your read-later app, a photo into your notes, text into your task list. An Action extension (com.apple.ui-services, shown in the sheet’s action row) transforms or acts on the content, such as translating selected text or running a script, and may return modified content to the host. The host passes content through an NSExtensionContext: its inputItems contain NSExtensionItems with attachments that are NSItemProviders, each advertising type identifiers (public.url, public.image, public.plain-text); you ask a provider to load an item for a type (loadItem(forTypeIdentifier:), or the async loadObject variants), which is asynchronous and may be a file URL, data or an image, and you declare which types you accept in Info.plist with an NSExtensionActivationRule (a predicate such as “at most one URL and any number of images”) so your extension only appears for content it can handle. A Share extension typically presents a compose view (an SLComposeServiceViewController or your own SwiftUI/UIKit view controller), and when done calls extensionContext?.completeRequest(returningItems:) to finish or cancelRequest(withError:) to abort, which dismisses it.

Constraints and good practice. The extension runs in its own process with a small memory budget, so decode large images with downsampling and never load a whole video into memory; do not do long uploads in the extension’s lifetime: save the item to the shared app-group container (and optionally a queue) and let the app, or a background URLSession configured with the shared container identifier, upload it (the extension can be killed as soon as completeRequest is called). The extension cannot open your app directly with UIApplication.shared.open; use extensionContext.open(_:) where supported, or deep-link by URL. Sign-in state comes from a shared Keychain access group. Handle the user cancelling, a missing network, and unsupported items without a crash. Share extensions and Action extensions can show a custom icon and title and must not mimic system UI. Document pickers and File Provider extensions let your app’s storage appear in the Files app and in open/save dialogs (the NSFileProviderExtension or the newer replicated NSFileProviderReplicatedExtension, where the system manages a local replica and you synchronise with your backend), and Photo Editing extensions apply edits inside the Photos app: both have the same cold-start and memory limits, and sync correctness is the hard part. Prefer App Intents and share targets for new sharing integrations where they fit, and test with several host apps, since each provides items differently.

The extension has a tiny budget and is killed soon after it completes; hand the file off through the app group and let a background session upload it.

Common traps

  • Uploading a large file inside the Share extension. The extension is killed soon after completion; hand it to the app or a background session.
  • Assuming an NSItemProvider returns data synchronously. Loading is asynchronous and the item type varies by host; handle URL, data and image forms.

They might follow up with

  • How does the extension limit when it appears? Through NSExtensionActivationRule in Info.plist.
  • How does a Share extension finish? completeRequest(returningItems:) or cancelRequest(withError:).

Related questions

  • 19.1.1 What is an app extension, how is it launched, and what limits do extensions run under?
  • 19.1.2 How do app groups, shared containers and keychain access groups let an app and its extensions share data, and…
  • 19.3.2 What do keyboards, message filtering, call directory, content blockers and web extensions, ReplayKit and…

What do keyboards, message filtering, call directory, content… →