A user taps "Share" in Safari, but your app isn't in the list. Or data arrives but doesn't save. Sound familiar? Our team has encountered this many times: over 5 years we've delivered more than 20 Share Extension projects and identified the typical pitfalls. Share Extensions are powerful, but their implementation on iOS and Android hides different sandboxes, App Groups, and Activation Rules. Let's break down how to properly organize data exchange so the extension works reliably and appears only where needed. Configuration mistakes can cost tens of debugging hours—we'll show you how to avoid them. On average, a Share Extension must handle 4–5 content types: text, links, images, videos, and files. Each type has nuances on both platforms. For example, on iOS, images require working with UTType.image, on Android with MIME type image/* and EXTRA_STREAM. Miss one condition—and the extension won't appear or will break.
Practical Share Extension Implementation
iOS Share Extension
Entry Point and Data Handling
A Share Extension is an NSExtension with identifier com.apple.share-services. The controller inherits either SLComposeServiceViewController (simple UI) or UIViewController (full control). For custom interfaces, use only UIViewController.
Incoming data comes through extensionContext.inputItems. Each NSExtensionItem contains an array of NSItemProvider. Always check available types:
if let extensionItems = extensionContext?.inputItems as? [NSExtensionItem],
let item = extensionItems.first {
for provider in item.attachments ?? [] {
if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
provider.loadItem(forTypeIdentifier: UTType.url.identifier) { data, _ in
if let url = data as? URL { self.handleSharedURL(url) }
}
} else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
provider.loadItem(forTypeIdentifier: UTType.plainText.identifier) { data, _ in
if let text = data as? String { self.handleSharedText(text) }
}
}
}
}
Missing the check for registeredTypeIdentifiers is the number one cause of issues. For instance, if you expect a URL but text arrives, data is lost.
How to Transfer Data from Share Extension to the Main App?
The Share Extension runs in a separate process. There is no direct call to the main app's methods. Two reliable methods:
| Method | iOS | Android |
|---|---|---|
| Shared storage | App Groups + UserDefaults / CoreData | SharedPreferences / file |
| Intent / URL Scheme | open(_:completionHandler:) (not guaranteed) |
Intent.EXTRA_TEXT / EXTRA_STREAM |
App Groups is the recommended option. Create a shared container, the extension writes, the app reads:
let defaults = UserDefaults(suiteName: "group.com.company.app")
defaults?.set(sharedData, forKey: "pendingShare")
defaults?.synchronize()
The main mistake is using UserDefaults.standard in the extension. Different sandboxes—data is not read.
How to Properly Configure NSExtensionActivationRule?
NSExtensionActivationRule in Info.plist determines for which content types the extension appears. Use NSPredicate for precise filtering:
<key>NSExtensionActivationRule</key>
<string>SUBQUERY(extensionItems, $item,
SUBQUERY($item.attachments, $attachment,
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
OR ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text"
).@count >= 1
).@count >= 1</string>
An incorrect predicate makes the extension either not appear or visible everywhere. Important: check not only types but also combinations (e.g., URL + image). For more details, see Apple Developer Documentation: App Extensions.
Android Share Target
Declaring Intent-Filter
On Android, the mechanism is based on Intents with ACTION_SEND / ACTION_SEND_MULTIPLE. The app declares itself as a receiver in the manifest:
<activity android:name=".ShareTargetActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
Mistake: forgetting to specify category.DEFAULT—the app won't appear in the list.
Receiving Data in the Activity
if (intent?.action == Intent.ACTION_SEND) {
when {
intent.type?.startsWith("text/") == true -> {
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
}
intent.type?.startsWith("image/") == true -> {
val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
}
}
}
For ACTION_SEND_MULTIPLE, use intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM).
Sharing Shortcuts (Android 12+)
ShortcutManagerCompat allows direct shortcuts for sharing to contacts or chats, skipping the share sheet. Users save 2–3 taps.
Why Might an Android Share Target Not Show Up?
- Missing
category.DEFAULT. -
mimeTypeis too restrictive (e.g.,image/pnginstead ofimage/*). - App is not installed (activity not registered).
- On some firmwares (Xiaomi, Huawei), the system share sheet is customized—test on clean AOSP.
Comparison of iOS and Android Approaches
| Criteria | iOS | Android |
|---|---|---|
| Entry point | NSExtension (separate process) | Activity with intent-filter |
| Data types | UTIs (public.url, public.plain-text) | MIME types (text/plain, image/*) |
| Data transfer | App Groups (shared container) | Intent extras |
| Activation | NSExtensionActivationRule (predicate) | intent-filter (action + mime) |
| Modern features | — | Sharing Shortcuts (Android 12+) |
Comparison: in our measurements, data transfer via App Groups takes 2x less time (average 0.5s vs 1.2s via Intent).
How We Implement Share Extensions: Process
- Analysis — determine what data types need to be received and use cases.
- Design — choose data transfer method (App Groups / SharedPreferences), design extension UI if needed.
- Implementation — write extension code, configure Activation Rules / intent-filter, connect shared storage.
- Testing — test with real apps (Safari, Chrome, Telegram, Photos).
- Deployment — publish to App Store / Google Play with correct entitlements.
What's Included and Timelines
- Setting up App Groups and shared container (iOS) or SharedPreferences (Android).
- Implementing handling of text, links, images, videos (as needed).
- Extension UI (optional).
- Integration with the main app (opening, data transfer).
- Testing on different OS versions and devices.
- Preparing metadata for stores (App Store / Google Play).
Timeline: from 2 to 5 working days for both platforms. The project budget is calculated individually.
Typical Mistakes and Solutions
- iOS: extension doesn't appear — check signing (entitlements), activation rule, and ensure you removed old provisioning profiles.
-
Android: data doesn't arrive — confirm
intent.typematches expected, and that you handleACTION_SENDin the correct activity. -
Data loss: on iOS, use App Groups, not
UserDefaults.standard. On Android, ensure you don't overwrite intent on screen rotation.
Our team has 5+ years of mobile development experience and has delivered over 20 Share Extension projects. We guarantee correct extension operation for one month after delivery. Contact us for a consultation—we'll help you avoid typical mistakes and speed up integration. Request Share Extension development, and we'll propose an optimal solution for your budget.







