- Published on
> Detect Successful Share Sheet Completion in SwiftUI
- Authors

- Name
- Mick MacCallum
- @0x7fs
SwiftUI's ShareLink view is the preferred way to present a share sheet, but it has a significant limitation: there's no way to know whether the user actually completed sharing. This can be problematic if you want to reward users for sharing your app, track analytics, unlock features conditionally, or trigger actions based on successful shares.
Some developers attempt to work around this by using simultaneousGesture() to detect taps on ShareLink, but this approach only tells you that the button was tapped, not whether the user followed through with sharing or simply cancelled the sheet. To get true completion feedback and know definitively whether content was shared, you need to bridge to UIKit's UIActivityViewController, which provides a robust completion handler with detailed information about the sharing outcome.
Wrapping UIActivityViewController
We can create a UIViewControllerRepresentable wrapper that exposes the completion handler from UIActivityViewController:
struct ActivityViewControllerView: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
var completionWithItemsHandler: UIActivityViewController.CompletionWithItemsHandler?
func makeUIViewController(
context: UIViewControllerRepresentableContext<ActivityViewControllerView>
) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities
)
controller.completionWithItemsHandler = { activityType, completed, returnedItems, error in
self.completionWithItemsHandler?(activityType, completed, returnedItems, error)
}
return controller
}
func updateUIViewController(
_ uiViewController: UIActivityViewController,
context: UIViewControllerRepresentableContext<ActivityViewControllerView>
) {}
}
Creating a ShareButton Component
To make this easier to use, we can create a reusable ShareButton view that handles the presentation and provides a cleaner API:
enum ShareButtonResult {
case shared(UIActivity.ActivityType?)
case cancelled
case failed(Error)
}
struct ShareButton<Label>: View where Label: View {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
var completion: @MainActor (ShareButtonResult) -> Void
@ViewBuilder var label: () -> Label
@State private var isSharePresented = false
var body: some View {
Button {
isSharePresented = true
} label: {
label()
}
.sheet(isPresented: $isSharePresented) {
ActivityViewControllerView(
activityItems: activityItems,
applicationActivities: applicationActivities
) { activityType, completed, _, error in
if let error {
completion(.failed(error))
} else if completed {
completion(.shared(activityType))
} else {
completion(.cancelled)
}
}
}
}
}
Note that it's unusual for a SwiftUI view to use a completion handler rather than state binding. This is a pragmatic workaround since we're bridging to UIKit's UIActivityViewController, which itself uses completion handlers.
Understanding the Completion Parameters
The UIActivityViewController.CompletionWithItemsHandler provides four parameters that help you understand what happened:
- activityType: An optional
UIActivity.ActivityTypeindicating which activity was used (e.g.,.message,.mail,.copyToPasteboard,.postToFacebook). This isnilif the user cancelled. - completed: A Boolean indicating whether the activity was completed successfully.
- returnedItems: Optional items returned by the activity. Most activities don't return items, so this is often
nil. - error: An optional error if the activity failed.
By examining the activityType, you can track which sharing methods users prefer, helping you optimize your app's sharing features or understand user behavior patterns.
Using the ShareButton
Here's how to use the ShareButton in your app:
struct ContentView: View {
@State private var shareCount = 0
var body: some View {
VStack(spacing: 20) {
Text("Shares: \(shareCount)")
.font(.headline)
ShareButton(
activityItems: ["Check out this awesome app!"],
completion: { result in
switch result {
case .shared(let activityType):
shareCount += 1
print("Shared via: \(activityType?.rawValue ?? "unknown")")
case .cancelled:
print("User cancelled sharing")
case .failed(let error):
print("Share failed: \(error.localizedDescription)")
}
},
label: {
Label("Share App", systemImage: "square.and.arrow.up")
}
)
}
.padding()
}
}
With this approach, you can track successful shares, reward users, update analytics, or trigger any other action based on whether the user actually completed the share action.
// Continue_Learning
SwiftUI vs UIKit: Which Should You Choose in 2026?
A practical guide to choosing between SwiftUI and UIKit in 2026, based on your project requirements, team experience, and the current state of both frameworks.
Accessing the Camera Roll in SwiftUI
Learn how to access photos from the camera roll in SwiftUI, including required permissions, Info.plist configuration, and using PhotosPicker for iOS 16+ or UIImagePickerController for earlier versions.
Detecting When a Screenshot is Taken in SwiftUI
Learn how to detect when users take screenshots in your SwiftUI app by observing UIApplication notifications.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.