iOS 16 introduced a system permission prompt for clipboard access. When an app reads from UIPasteboard.general, iOS shows a dialog: "[App] would like to paste from [Source]." The user can allow or deny. This change directly affects clipboard-based deferred deep linking, where a token is copied to the clipboard on a web click and read by the app after installation.
Before iOS 16, apps could read the clipboard silently (with only a banner notification on iOS 14+). Now, the user must actively consent. This creates a UX challenge: the first thing a new user sees after installing your app is a system prompt asking for paste permission, with no context about why.
This guide covers how to handle the paste permission prompt effectively, alternative approaches, and UX patterns that maintain a good first-launch experience. For the broader clipboard deferred linking flow, see clipboard-based deferred linking. For privacy implications, see deferred linking privacy considerations.
The iOS Paste Permission Timeline
Understanding how Apple has progressively tightened clipboard access:
iOS 13 and earlier: Apps could read the clipboard at any time, silently. No user notification.
iOS 14 (2020): Apple added a paste notification banner: "[App] pasted from [Source App]." This notification is informational; it does not block the paste. It appears briefly at the top of the screen. This was Apple's response to reports of apps reading clipboard data excessively.
iOS 15: Same behavior as iOS 14. Banner notification on paste.
iOS 16 (2022): Apple introduced the paste permission prompt. Instead of a passive banner, a modal dialog appears: "[App] would like to paste from [Source App]. Allow Paste?" with "Don't Allow" and "Allow Paste" buttons. The app cannot read clipboard content until the user taps "Allow Paste."
iOS 17+: Same permission prompt behavior. Apple also introduced UIPasteControl improvements and additional clipboard privacy features.
The UX Problem
The paste permission prompt creates a friction point in the deferred deep linking flow:
1. User taps link on web → token copied to clipboard
2. User installs app from App Store
3. User opens app for the first time
4. App attempts to read clipboard
5. iOS shows: "[App] would like to paste from Safari. Allow Paste?"
6. User thinks: "Why does this app want to paste? I didn't copy anything."
7. User taps "Don't Allow"
8. Deferred link match fails
The problem: the user does not remember copying anything. The clipboard write happened transparently as part of the link click. The paste permission prompt appears without context, and most users instinctively deny unfamiliar permissions.
Studies and industry reports suggest paste permission acceptance rates range from 30-50% without pre-prompting, and 50-70% with a well-designed explanation screen shown before the system prompt.
UX Patterns for Better Acceptance Rates
Pattern 1: Pre-Prompt Explanation Screen
Show an in-app screen before triggering the paste permission. Explain what will happen and why:
class WelcomeViewController: UIViewController {
func handleFirstLaunch() {
// Check if we likely have a deferred link to resolve
// (e.g., the user came from an ad campaign or shared link)
showDeferredLinkExplanation()
}
func showDeferredLinkExplanation() {
let alert = UIAlertController(
title: "Welcome! Were you sent here by a friend?",
message: "If someone shared a link with you, we can take you directly to the right place. Just tap 'Allow Paste' on the next prompt.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Yes, take me there", style: .default) { _ in
self.attemptClipboardMatch()
})
alert.addAction(UIAlertAction(title: "No, just get started", style: .cancel) { _ in
self.proceedToOnboarding()
})
present(alert, animated: true)
}
func attemptClipboardMatch() {
let pasteboard = UIPasteboard.general
// This triggers the system paste permission prompt
guard let text = pasteboard.string else {
proceedToOnboarding()
return
}
if let token = parseDeferredLinkToken(text) {
routeToContent(token)
} else {
proceedToOnboarding()
}
}
}
The pre-prompt gives the user context. They understand why the system prompt appears, and they are more likely to allow it.
Pattern 2: UIPasteControl
UIPasteControl is a system-provided button that grants clipboard access when tapped, without showing the permission prompt. The tap itself is the consent.
class OnboardingViewController: UIViewController, UIPasteConfigurationSupporting {
var pasteConfiguration: UIPasteConfiguration? = UIPasteConfiguration(
acceptableTypeIdentifiers: [UTType.plainText.identifier]
)
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 16.0, *) {
let config = UIPasteControl.Configuration()
config.displayMode = .labelOnly
config.cornerStyle = .capsule
config.baseBackgroundColor = .systemBlue
config.baseForegroundColor = .white
let pasteButton = UIPasteControl(configuration: config)
pasteButton.frame = CGRect(x: 50, y: 300, width: 250, height: 50)
pasteButton.target = self
view.addSubview(pasteButton)
}
}
override func paste(itemProviders: [NSItemProvider]) {
for provider in itemProviders {
provider.loadObject(ofClass: String.self) { text, error in
guard let text = text else { return }
DispatchQueue.main.async {
if let token = self.parseDeferredLinkToken(text) {
self.routeToContent(token)
}
}
}
}
}
}
Advantages of UIPasteControl:
- No system permission prompt (the tap is implicit consent)
- Apple-sanctioned approach for clipboard access
- Clear user intent
Disadvantages:
- You cannot customize the button's appearance beyond basic properties (color, corner style, label)
- The button must be visible and tappable by the user (you cannot trigger it programmatically)
- You need to integrate it into your onboarding flow naturally
- It requires the user to take an explicit action (tap the button)
Pattern 3: Deferred Prompt (Check Later)
Do not check the clipboard on first launch. Instead, wait for a natural moment when the user would expect paste behavior:
class ContentViewController: UIViewController {
func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Check clipboard only if the user navigates to a "redeem" or "enter code" screen
if isFirstLaunch && hasRedeemableContent() {
checkClipboardForToken()
}
}
func checkClipboardForToken() {
// This is a natural context for paste; users expect it here
let pasteboard = UIPasteboard.general
if let text = pasteboard.string, let token = parseDeferredLinkToken(text) {
showConfirmation("We found a shared link. Go to \(token.contentDescription)?")
}
}
}
This pattern works well for apps where entering a code or redeeming a link is a natural action (referral programs, gift cards, invitation codes).
Pattern 4: Skip Clipboard Entirely
If your paste permission acceptance rate is too low to justify the UX friction, skip clipboard matching and use alternative deferred linking methods:
- Android: Rely on the Play Install Referrer (more reliable anyway).
- iOS: Use server-side probabilistic matching as the primary method, accepting the lower accuracy.
- Both: Use campaign-level attribution (UTM parameters in app store URLs) instead of user-level deferred linking.
This is a valid choice when the deferred link content is not critical to the user's first experience.
Detecting Whether to Show the Prompt
You do not want to show the paste permission prompt to users who did not come from a deferred link. Showing it to every new user, regardless of acquisition channel (organic, search, direct install), creates unnecessary friction.
Heuristics for Filtering
func shouldAttemptClipboardMatch() -> Bool {
// Only attempt on first launch
guard isFirstLaunch() else { return false }
// Check if we have any indication the user came from a link
// This could be a server-side flag, a campaign parameter, etc.
// Check if clipboard contains something that looks like our token
// Note: on iOS 16+, even checking UIPasteboard.general.hasStrings
// may trigger the paste notification (but not the full prompt)
if #available(iOS 16.0, *) {
// On iOS 16+, we can check if pasteboard has strings without
// triggering the full permission prompt
return UIPasteboard.general.hasStrings
} else {
return true
}
}
Note: UIPasteboard.general.hasStrings returns a boolean without reading the actual content. On iOS 16+, this check does not trigger the paste permission prompt, but it may show the paste banner on iOS 14-15. Use it as a preliminary filter.
Server-Side Hints
A more reliable approach: when the user clicks your link on the web, record the click server-side with a session identifier. When the app opens, send a lightweight "did I come from a link?" check to your server using non-identifying information (e.g., a first-party cookie if available). If the server confirms a recent click, then show the clipboard prompt.
This avoids showing the paste prompt to organic installs entirely.
Testing the Paste Permission
Simulator vs. Device
The iOS Simulator does not accurately replicate paste permission behavior. Always test on a physical device:
- Copy a token to the clipboard (in Safari on the device).
- Install your app (via Xcode or TestFlight).
- Launch the app.
- Verify the paste permission prompt appears.
- Test both "Allow Paste" and "Don't Allow" flows.
Resetting Permission State
To test the paste permission prompt again after allowing it:
- Uninstall and reinstall the app. The paste permission is per-app-install, not permanent.
- On iOS 16+, the permission prompt appears each time the app reads the clipboard after a new install (not persisted across launches for the same install, but behavior varies).
Automated Testing
For unit tests, mock UIPasteboard:
protocol PasteboardProvider {
var string: String? { get }
var hasStrings: Bool { get }
}
extension UIPasteboard: PasteboardProvider {}
class MockPasteboard: PasteboardProvider {
var string: String?
var hasStrings: Bool = false
}
// In your deferred link handler:
class DeferredLinkHandler {
let pasteboard: PasteboardProvider
init(pasteboard: PasteboardProvider = UIPasteboard.general) {
self.pasteboard = pasteboard
}
func checkForDeferredLink() -> DeferredLink? {
guard let text = pasteboard.string else { return nil }
return parseDeferredLinkToken(text)
}
}
Tolinku's Approach
Tolinku's iOS SDK handles the clipboard permission flow automatically. The SDK checks whether a clipboard match is likely before triggering the system prompt, reducing unnecessary permission requests for organic installs. When the prompt is needed, the SDK provides hooks for showing a pre-prompt explanation screen.
Configure your deferred linking behavior in the Tolinku dashboard. For the broader deferred linking flow, see how deferred deep linking works. For clipboard-based matching details, see clipboard-based deferred linking.
Get deep linking tips in your inbox
One email per week. No spam.