Your push notification opt-in rate is the first serious trust test your app runs with the user: the system dialog appears once, the user taps "Allow" or "Don't Allow," and that decision is usually permanent. In this article, based on Apple and Android's official documentation, you'll walk step by step through priming screens, correct timing, provisional authorization, and how to measure your opt-in rate.
💡 Pro Tip: Before you show the permission dialog, ask yourself — does the user currently know _what the notification will actually do for them_? If the answer is no, you shouldn't show the dialog yet.
Table of Contents
- The System Dialog's One Shot: Why It's Hard to Reverse
- iOS: Ask Once, Never Again
- Android: Restricted Access After Denial on 12L and Below
- The Priming Screen: Asking Permission in Context
- Why Context Matters
- Timing: Finding the Moment of Value
- iOS: After the First Concrete Action
- Android: Familiarization Period and Action Triggers
- How to Validate Your Timing Decision
- When Provisional Authorization Works
- How the `provisional` Option Works
- The Settings Redirect Flow for Users Who Decline
- Measurement: Opt-In Rate, Delivery, Opens, and Mute
- 6 Common Mistakes
- FAQ
- How do you increase the push notification opt-in rate?
- When should I ask for permission?
- Can a denied permission be recovered?
- Is provisional authorization suitable for every notification type?
- Is the opt-in rate the same as the delivery rate?
- Does a priming screen really increase the opt-in rate?
- Update (September 2026)
- Conclusion
- Sources
The System Dialog's One Shot: Why It's Hard to Reverse
On iOS, the system permission dialog is, in practice, a one-time event. Apple's own documentation states this plainly: calls after the first request don't prompt the person again.
iOS: Ask Once, Never Again
Apple's UserNotifications framework documentation says: "Subsequent authorization requests don't prompt the person." When you call requestAuthorization a second time, the system won't show a new dialog — it just returns the previous decision (granted or denied). If the user wants to change their mind, the only path is Settings.
This is a separate topic from the APNs delivery mechanism covered in the advanced-push-notifications article — there the focus is "how do I send it," here it's "how do I get permission." For detailed APNs and rich media setup, see Advanced Push Notifications: Everything from APNs to Rich Media.
Apple also notes it doesn't guarantee delivery: "The system makes every attempt to deliver local and remote notifications in a timely manner, but delivery isn't guaranteed." So raising your opt-in rate doesn't automatically raise your delivery rate — track them as separate metrics.
Android: Restricted Access After Denial on 12L and Below
On Android, behavior depends on your target SDK version. Google's official documentation says: "If your app targets 12L or lower and the user taps Don't allow, even just once, they aren't prompted again until they uninstall and reinstall your app, or you update your app to target Android 13 or higher." So on API 32 and below, a single "Don't Allow" tap is a valid denial until the user uninstalls and reinstalls, or you raise targetSdkVersion to 33+. The system typically shows this dialog on the first activity launch after you create your first notification channel — in Google's words, "usually on app startup."
The POST_NOTIFICATIONS runtime permission introduced with Android 13 (API 33) softens this behavior somewhat, but the core rule stays the same: once a user declines, your ability to ask again is restricted by the system (or restored by raising targetSdk). That's why, on both platforms, "asking it right the first time" is your only real shot.
Before moving to strategy, it's worth laying the two platforms' permission models side by side — because timing and win-back tactics are shaped by these differences:
Feature | iOS | Android (13+) |
|---|---|---|
When the permission dialog appears | When requestAuthorization is called, developer-controlled | Developer-controlled if targetSdkVersion is 33+ |
Can it be asked again after denial | No, only via Settings | No, only via Settings |
Is there a silent/trial mode | Yes — provisional | No |
Legacy targeting (12L/below API 32) behavior | Not applicable (same model across all versions) | System shows it automatically, usually at launch |
App behavior after denial | Notifications can't be sent, app keeps working | All channels are blocked except for apps managing media session notifications and their own calls via CallStyle |
This table is a quick summary of why the sections ahead recommend separate timing and win-back strategies for iOS and Android.
The Priming Screen: Asking Permission in Context
A priming screen is your own custom explanation screen shown before the system dialog. Its purpose is simple: let the user find the answer to "why" before they see the system dialog.
Why Context Matters
Apple's own recommendation is clear: "Make the request in a context that helps people understand why your app needs authorization." And it continues with a comparison: "Sending the request in context provides a better experience than automatically requesting authorization on first launch, because people can see the purpose your app's notifications serve."
These two sentences are essentially the whole strategy in a nutshell: don't request permission automatically the moment the app opens. Instead, wait until the user has _experienced_ the value of the notification, then ask.
Things to pay attention to on a priming screen:
- Write a concrete benefit: not "Can we send you notifications?" but a clear promise like "We'll let you know the moment your order arrives at your door."
- Leave an exit: a screen with no "Not now" option forces the user straight into the system dialog and makes a denial permanent.
- Focus on a single ask: if location + notifications + camera permission are requested on the same screen, all of them risk being denied together.
Timing: Finding the Moment of Value
As decisive as the priming screen's content is _when_ it's shown. Both platforms describe the same principle in different words: ask after the user has experienced a moment of value.
iOS: After the First Concrete Action
Apple gives the example of a task-tracking app: "In a task-tracking app that sends reminder notifications, you might make the request after the person schedules a first task." In other words, ask for permission right after the user sets up their first reminder — they just defined for themselves what the notification is good for.
1func scheduleFirstReminder(for task: ReminderTask) {2 NotificationScheduler.saveLocally(task)3 4 // The user just scheduled their first task — they defined the5 // notification's value themselves, this is the right moment to ask.6 UNUserNotificationCenter.current().requestAuthorization(7 options: [.alert, .sound, .badge]8 ) { granted, error in9 guard granted else { return }10 NotificationScheduler.scheduleReminder(for: task)11 }12}Android: Familiarization Period and Action Triggers
Google's recommendation points the same direction: "Before you ask users to grant any permissions, let them familiarize themselves with your app." Google also gives concrete trigger examples: "The user taps an 'alert bell' button. The user chooses to follow someone's social media account. The user submits an order for food delivery." As an alternative, a time-based threshold is also suggested: "you might wait until the third or fourth time the user launches your app."
For apps targeting Android 13 and above, this timing is entirely under your control: "If your app targets Android 13 or higher, your app has complete control over when the permission dialog is displayed." For older apps that don't target it, the system permission is typically shown automatically at app launch — which is why raising targetSdkVersion to 33+ is a prerequisite for any timing strategy.
1class OnboardingFlow(private val activity: Activity) {2 3 // The user just submitted their first order — we're at the4 // "action-triggered" moment Google recommends, so we ask here.5 fun onOrderSubmitted() {6 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {7 val hasPermission = ContextCompat.checkSelfPermission(8 activity, Manifest.permission.POST_NOTIFICATIONS9 ) == PackageManager.PERMISSION_GRANTED10 11 if (!hasPermission) {12 ActivityCompat.requestPermissions(13 activity,14 arrayOf(Manifest.permission.POST_NOTIFICATIONS),15 NOTIFICATION_PERMISSION_REQUEST_CODE16 )17 }18 }19 }20 21 companion object {22 const val NOTIFICATION_PERMISSION_REQUEST_CODE = 100123 }24}Don't forget to add the permission to AndroidManifest.xml — in Google's words, this is the permission that you need to declare in your app's manifest file:
1<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />How to Validate Your Timing Decision
Timing recommendations ("after the first task," "on the third launch") are a starting point, not a hard rule. To find the right moment in your own app, you can follow these steps:
- Define your app's critical moment of value (the "aha" action — a first order, first message, first reminder).
- Place the permission request right after this moment, ideally as part of the same screen flow.
- Track the opt-in rate separately across two different cohorts (e.g., "after action" vs. "on the third launch") and compare them.
- Make the decision over at least a one- or two-week window, not a single day — user behavior can fluctuate day to day.
Once you make this loop repeatable, "correct timing" stops being a guess and becomes a decision you validate with your own data.
When Provisional Authorization Works
On iOS there's a third path: start a silent trial period without ever asking the user at all.
How the `provisional` Option Works
Apple's definition is clear: "Use provisional authorization to send notifications on a trial basis." When this option is added, the system automatically grants authorization without asking the user anything: "the first time you call this method, it automatically grants authorization." The API reference definition confirms this too: "The ability to post noninterrupting notifications provisionally to the Notification Center."
Provisional notifications behave differently — Apple explains: "The system delivers provisional notifications quietly — they don't interrupt the person with a sound or banner, or appear on the lock screen." No sound, no banner, no lock screen — they simply collect in Notification Center. So that the person can decide once they see them, buttons are also added: "These notifications also include buttons that prompt the person to keep or turn off the notification."
1func requestProvisionalAuthorization() {2 let center = UNUserNotificationCenter.current()3 4 // On the first call, the system grants authorization automatically5 // and shows the user no dialog — notifications collect silently.6 center.requestAuthorization(options: [.alert, .badge, .sound, .provisional]) { granted, error in7 if let error = error {8 print("Provisional authorization error: \(error)")9 }10 }11}Where provisional works well, and where it doesn't:
Scenario | Is provisional a fit? | Why |
|---|---|---|
Order/delivery status notifications | Yes | The user can view and evaluate a low-risk, informational flow in Notification Center |
Marketing/campaign notifications | No | Getting explicit consent via the system dialog preserves trust in the long run |
Time-critical alerts (e.g., security codes) | No | Since it's delivered without sound/banner, the user may not notice it in time |
New feature trial | Yes | The user can experience it and decide via "keep/turn off" |
Provisional isn't an "opt-in rate hack" — the user still makes a "keep/turn off" decision on every notification. Its purpose is to reduce the first-contact friction of the system dialog, making the move to full authorization easier _after_ showing the user the notification's value.
The Settings Redirect Flow for Users Who Decline
Once a user declines, the system dialog can't be triggered again from inside the app; the only path is redirecting the user to the platform's own Settings screen. Apple summarizes the technical basis for this: "Always check your app's authorization status before scheduling local notifications." So first read the current status, then act based on it.
If you want to redirect the user after this check, both platforms offer a standard, documented system API for it: on iOS, UIApplication.openSettingsURLString, and on Android, the Settings.ACTION_APP_NOTIFICATION_SETTINGS intent opens the app's own notification settings page. Always read the current status before showing this flow in the UI — sending a user who hasn't declined to Settings unnecessarily worsens the experience.
On Android, the consequences of denial are stricter. Google summarizes it like this: "If the user selects the don't allow option, your app can't send notifications unless it qualifies for an exemption." In the page's own words, these exemptions are limited: "All notification channels are blocked, except for a few specific roles." Concretely, only two: media-session notifications ("Notifications related to media sessions are exempt from this behavior change.") and apps managing their own calls via Notification.CallStyle (MANAGE_OWN_CALLS + ConnectionService + registerPhoneAccount together, no POST_NOTIFICATIONS needed). Foreground service notifications aren't exempt: once denied, they no longer appear in the notification drawer, only in the Task Manager — making "redirect to Settings" a more critical win-back mechanism on Android than on iOS.
Measurement: Opt-In Rate, Delivery, Opens, and Mute
To measure the opt-in rate correctly, you first need to distinguish what each signal actually tells you. Apple's API returns a device-side status to you; this isn't an analytics platform, but it is the only reliable way to verify permission status.
1UNUserNotificationCenter.current().getNotificationSettings { settings in2 // The actual state on the device — not a server-side guess.3 // Apple's own measurement example checks authorized and provisional4 // together; log them SEPARATELY here so provisional users5 // aren't counted as "unauthorized" and skew the report.6 let isAuthorized = settings.authorizationStatus == .authorized7 let isProvisional = settings.authorizationStatus == .provisional8 let alertsEnabled = settings.alertSetting == .enabled9 10 AnalyticsLogger.log(11 event: "notification_status_checked",12 properties: [13 "authorized": isAuthorized,14 "provisional": isProvisional,15 "alerts_enabled": alertsEnabled16 ]17 )18}Tracking these four signals by cohort, not as a one-off snapshot, gives a far more meaningful result. Comparing the opt-in rate of users who signed up this week against last month, for example, shows whether a priming-screen copy change actually worked. A single day's snapshot is easily skewed by weekday/weekend noise or campaign periods.
A practical method: tag the user with a cohort ID from their first session, log the permission request's shown/answered moments as events, then report the opt-in rate (authorized / total requests shown) weekly per cohort. This isolates the impact of every priming-screen change you make.
It's important not to mix up these four signals — each answers a different question:
Signal | What it measures | How it's read |
|---|---|---|
Opt-in rate | How many users tapped "Allow" | authorizationStatus (authorized + provisional counted separately) / POST_NOTIFICATIONS result, on-device |
Delivery | Did the notification reach the device | Server-side send log + APNs/FCM response code |
Opens | Did the user tap the notification | In-app event keyed by a unique ID in the notification payload |
Mute/disable | Did the user turn off the notification | alertSetting/channel status, checked periodically |
As Apple also reminds us, delivery isn't guaranteed — so keeping "sent" and "delivered" as separate metrics lets you see clearly what your opt-in rate optimization actually changed.
6 Common Mistakes
- Automatic first-launch request: the exact opposite of Apple's "ask in context" advice — the user hits the dialog before even understanding what the app is, and usually declines.
- Relying on the system's automatic timing when targeting 12L and below: on Android this means an ask made without building your own context; a single denial stays valid until the app is uninstalled and reinstalled, or
targetSdkVersionis raised to 33+. - Trying to send notifications without adding
POST_NOTIFICATIONSto the manifest or requesting it at runtime: notifications won't reach the user until they approve the permission — this doesn't throw an error, so you might not notice it in the logs. - Abusing provisional to "inflate" the opt-in rate: sending critical or time-sensitive notifications provisionally means they stay silent exactly when the user most needs to see them.
- Asking for all permissions on a single screen: requesting notification + location + camera in the same flow accumulates the risk of denial; ask each permission in its own context, separately.
- Re-sending a request without ever checking status: calling
requestAuthorizationagain without callinggetNotificationSettings/checkSelfPermissionfirst is dead code that shows nothing to a user who has already declined.
GOLDEN TIP
The most valuable insight in this article
This tip holds the article's most important takeaway.
Easter Egg
You found a hidden gem!
There's a hidden detail in this section. Want to uncover it?
Reader Reward
There are items you should check before pushing your permission request flow to production. Check off this list one by one before launch; each item left unchecked makes it more unclear what the opt-in rate you're measuring actually represents.
FAQ
How do you increase the push notification opt-in rate?
The foundation of raising the opt-in rate is showing the system dialog at the moment the user has experienced the notification's value. This is Apple and Android's shared recommendation: ask in context, right after the first concrete action, not automatically on first launch. On iOS, provisional authorization also lets you quietly introduce low-risk notifications and let the user decide for themselves.
When should I ask for permission?
After the user has completed at least one meaningful action in your app. Apple illustrates this with "after the person schedules their first task"; Google recommends "the third or fourth launch" or a clear action trigger (placing an order, following someone). Apple also notes that even if you carefully build the context, the user may not have enough information to decide and could still deny the request.
Can a denied permission be recovered?
Not from inside the app. Once the system dialog has been answered, it can't be triggered again; the only path is redirecting the user to the platform's own notification settings screen (openSettingsURLString on iOS, ACTION_APP_NOTIFICATION_SETTINGS on Android). For apps targeting Android 12L and below, a single denial stays valid until the app is uninstalled and reinstalled, or targetSdkVersion is updated to 33+.
Is provisional authorization suitable for every notification type?
No. Provisional is designed for silent, low-risk notifications — it shows no sound/banner and doesn't appear on the lock screen. For time-critical or security-related notifications, the user might not notice it, so requesting full authorization is more appropriate for those.
Is the opt-in rate the same as the delivery rate?
No, two different metrics. The opt-in rate shows how many users tapped "Allow"; delivery shows whether the notification actually reached the device. Even Apple's docs state delivery isn't guaranteed, so a high opt-in rate alone doesn't mean high delivery — track them separately.
Does a priming screen really increase the opt-in rate?
Yes, when done right — but there's no guarantee, which is why you should run the before/after test recommended in this article's Golden Tip on your own app. Apple's recommendation is clear in one direction: it states that asking in context provides a better experience than an automatic first-launch request. But "better experience" and "higher opt-in rate" don't always map one to one; the screen's copy, timing, and the quality of its exit option all shape the outcome.
Update (September 2026)
This article's body describes platform behavior as of 2024-12-11. Since then, one development directly concerns opt-in rate strategy:
- Android 16 (June 10, 2025) introduced progress-focused notifications with
Notification.ProgressStyle; Google describes this as the foundation of Live Updates, and says Live Updates will be completed in a later Android 16 update. This adds a new option to the "which notification type belongs in which priority tier" question in your permission/category strategy.
This change didn't modify the provisional API or the POST_NOTIFICATIONS runtime permission — this article's technical core still holds; the above is simply a new layer on top of the strategy.
Related posts published later:
- TipKit: A Guide to iOS Onboarding and Feature Discovery Patterns
- App Store Optimization (ASO): Get Your App Discovered
- I Made $1M on the iOS App Store: The Real Strategy and Numbers
- WorkManager 2.10 Coroutines: A Modern Approach to Background Tasks
Conclusion
The push notification opt-in rate isn't the result of a single screen, but of your entire onboarding flow: correct timing, a contextual priming screen, and (on iOS) proper use of provisional authorization together reduce the risk of a permanent denial. A ready win-back flow for users who decline also keeps this rate from eroding over time.
For the technical delivery side, see Advanced Push Notifications: Everything from APNs to Rich Media.
Sources
- Apple — Asking permission to use notifications — the one-time nature of the system dialog, the recommendation to ask in context, and the definition of provisional authorization.
- Apple — UserNotifications framework overview — official statement that the notification delivery mechanism isn't guaranteed.
- Apple — UNAuthorizationOptions.provisional API reference — the API definition of the provisional option.
- Android Developers — Notification runtime permission — the
POST_NOTIFICATIONSpermission, behavior on 12L and below, timing recommendations. - Android Developers Blog — Android 16 is here — Android 16's general release date,
Notification.ProgressStyle, and its explanation as the foundation of Live Updates.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

