<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Amar Spahic · Remote Since Forever</title>
  <subtitle>Essays by Amar Spahic on Remote Since Forever — the Clearview Team blog.</subtitle>
  <link href="https://blog.clearview.team/authors/amar-spahic/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://blog.clearview.team/" />
  <updated>2026-08-20T17:39:48+02:00</updated>
  <id>https://blog.clearview.team/authors/amar-spahic/feed.xml</id>
  <author>
    <name>Amar Spahic</name>
  </author>
  <entry>
    <title>Asking For Notification Permission: The Second Time Is the Wrong Time</title>
    <link href="https://blog.clearview.team/2026/notification-permission-priming/" />
    <id>https://blog.clearview.team/2026/notification-permission-priming/</id>
    <published>2026-07-14T11:00:00+02:00</published>
    <updated>2026-07-14T11:00:00+02:00</updated>
    <author>
      <name>Amar Spahic</name>
    </author>
    <summary>iOS gives you exactly one shot at the native permission prompt. Here&apos;s the simple screen we show first, so users see what they&apos;ll get before the OS asks the question.</summary>
    <content type="html">&lt;p&gt;I work on a B2C health app where notifications aren&apos;t a growth channel, they&apos;re half the product. The user sets a sleep goal, the app reminds them to wind down before bedtime. They pick a protocol with a daily action, the app nudges them to do it. Without notification permission all of that just doesn&apos;t exist for the user, they set goals in onboarding and then nothing ever follows up on them.&lt;/p&gt;

&lt;p&gt;Our first onboarding didn&apos;t treat the permission with that much respect. We did it the way most apps do, at some point in the flow we called the API, the iOS dialog popped up with no context, and the user had to decide on the spot why an app they installed five minutes ago wants to send them things. Predictably, a lot of them tapped &quot;Don&apos;t Allow&quot;.&lt;/p&gt;

&lt;p&gt;We assumed we could ask again later, once the user had seen enough of the app to change their mind. That assumption was wrong, and it&apos;s the reason this post exists. On iOS the native prompt is a one-shot, once the user says no, calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestPermissionsAsync()&lt;/code&gt; again just returns &quot;denied&quot; without showing anything. The only recovery is the user going to Settings, Notifications, Your App and flipping the switch by hand, and very few people who decline ever do that.&lt;/p&gt;

&lt;p&gt;So every &quot;Don&apos;t Allow&quot; was a permanent opt-out from a core part of the product, which made the decline rate the most expensive metric in the whole onboarding.&lt;/p&gt;

&lt;p&gt;Let me show you what we did instead, the screen we put in front of the prompt, the hook that guards the request, and what we&apos;re still measuring.&lt;/p&gt;

&lt;h2 id=&quot;what-the-ios-docs-dont-tell-you&quot;&gt;What the iOS docs don&apos;t tell you&lt;/h2&gt;

&lt;p&gt;The native prompt is a one-shot, that part is documented. What isn&apos;t documented as prominently is when it becomes a one-shot. It&apos;s not the first time you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestPermissionsAsync()&lt;/code&gt;, it&apos;s the first time the user sees it. Once they&apos;ve seen the prompt and declined, you can&apos;t show it again from your app code.&lt;/p&gt;

&lt;p&gt;You can detect the state with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getPermissionsAsync()&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Notifications&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;expo-notifications&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getPermissionsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &quot;granted&quot; | &quot;denied&quot; | &quot;undetermined&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;undetermined&quot;&lt;/code&gt; is the only state where calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestPermissionsAsync()&lt;/code&gt; will actually show the system prompt. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;denied&quot;&lt;/code&gt; means the user already saw it and said no, and your only recovery is the Settings deep link. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;granted&quot;&lt;/code&gt; means you&apos;re clear to send notifications.&lt;/p&gt;

&lt;p&gt;The whole pattern in this post is built on one rule: never call request while the status is undetermined unless you&apos;re confident the user will say yes.&lt;/p&gt;

&lt;h2 id=&quot;timing-a-lesson-from-game-design&quot;&gt;Timing: a lesson from game design&lt;/h2&gt;

&lt;p&gt;Mobile games solved this years ago. A well-designed game never asks for notification permission on first launch, it waits until you&apos;ve finished the first level, earned your first reward, or started a building that takes an hour to complete. At that moment the notification has an obvious job, &quot;we&apos;ll tell you when it&apos;s done&quot;, and the player has already felt the value, so the ask answers a question they actually have.&lt;/p&gt;

&lt;p&gt;The worst time to ask is before the user has experienced anything, at that point the permission prompt is a cost with no visible benefit and &quot;Don&apos;t Allow&quot; is the rational answer. The best time is right after a moment of value, when the notification is clearly in service of something the user just chose.&lt;/p&gt;

&lt;p&gt;That&apos;s why our screen sits where it does in the onboarding. The user has already entered their name, picked their goals and invested a few minutes in the flow, so the notification ask is framed as the thing that makes those goals happen. Same principle as the game asking after the first level, earn the moment first, then ask.&lt;/p&gt;

&lt;h2 id=&quot;the-screen-we-show-before-the-prompt&quot;&gt;The screen we show before the prompt&lt;/h2&gt;

&lt;p&gt;The screen we shipped is one step in the onboarding flow, right after the user has entered their name, picked their goals and invested a few minutes in the flow. It&apos;s deliberately simple, a title, a short description and two buttons:&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NotificationExplainerScreen&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Stay on track&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;We&apos;ll send you gentle reminders for your daily habits, lab result updates the moment they&apos;re ready, and progress milestones worth celebrating. One nudge at the right time, never spam. You&apos;re in control, turn off or customize every notification type anytime in Settings.&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;primaryAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Turn on notifications&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;onPress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requestNotificationPermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secondaryAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Skip for now&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onPress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;advanceOnboarding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every line of that copy is doing a job, and most of the jobs come straight from game design.&lt;/p&gt;

&lt;p&gt;&quot;Gentle reminders for your daily habits&quot; names the concrete thing the user just set up, the goals from two screens ago, so the notification is in service of something they chose. &quot;Progress milestones worth celebrating&quot; is the reward loop, games learned a long time ago that people say yes to notifications about wins, a level completed, a streak kept, much more readily than to notifications about obligations. &quot;Lab result updates the moment they&apos;re ready&quot; gives the notification an obvious job, the same way a game asks for permission when your building has an hour left, we&apos;ll tell you when it&apos;s done.&lt;/p&gt;

&lt;p&gt;And &quot;you&apos;re in control, turn off or customize anytime in Settings&quot; lowers the perceived cost of saying yes. Good games always let you toggle notification types individually, and telling the user up front that they keep that control makes &quot;Turn on notifications&quot; feel reversible instead of a commitment.&lt;/p&gt;

&lt;p&gt;Then there&apos;s &quot;Skip for now&quot;, and this one needs explaining because it looks like a mistake. It isn&apos;t. Skipping our screen costs nothing, the permission status stays undetermined, so we can ask again later at a better moment, after the user&apos;s first completed habit or their first lab result. Declining the OS prompt costs everything, it burns the one-shot permanently. So the skip button is there on purpose, we&apos;d much rather collect a cheap &quot;not now&quot; on our own screen than push an unsure user into an expensive, irreversible &quot;Don&apos;t Allow&quot; on Apple&apos;s.&lt;/p&gt;

&lt;h2 id=&quot;the-hook&quot;&gt;The hook&lt;/h2&gt;

&lt;p&gt;The screen calls a single hook on continue:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// hooks/useNotificationSetup.ts — abridged&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Notifications&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;expo-notifications&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useNotificationSetup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requestNotificationPermission&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getPermissionsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;granted&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Already on. Skip the prompt; advance the onboarding.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;granted&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;denied&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Already declined. Show the &quot;open Settings&quot; sheet instead of the prompt.&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;showSettingsSheet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;denied&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// status === &quot;undetermined&quot; — the one case where the prompt will actually fire.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestPermissionsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ios&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;allowAlert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;allowBadge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;allowSound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;allowAnnouncements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;granted&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;registerForPushToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;requestNotificationPermission&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Three guards in three lines. &quot;Granted&quot; skips the prompt entirely, &quot;denied&quot; opens the Settings deep-link sheet so the user can recover, and &quot;undetermined&quot; is the only path that calls the OS request.&lt;/p&gt;

&lt;p&gt;Asking for the four iOS flags explicitly is worth doing. We didn&apos;t enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allowAnnouncements&lt;/code&gt;, that&apos;s the Siri &quot;read this aloud through AirPods&quot; permission and the product doesn&apos;t need it. Asking for permissions you don&apos;t need just nudges the user toward decline.&lt;/p&gt;

&lt;h2 id=&quot;the-settings-sheet-for-the-users-who-already-said-no&quot;&gt;The Settings sheet, for the users who already said no&lt;/h2&gt;

&lt;p&gt;We show a separate small sheet for users whose status is already &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;denied&quot;&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BottomSheet&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Notifications are off&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;You said no the first time, which is fine. The only way to turn them on now is in your phone&apos;s Settings.&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;primaryAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Open Settings&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;onPress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Linking&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;openSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secondaryAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Maybe later&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onPress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dismiss&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The copy matters here. &quot;You said no the first time, which is fine&quot; respects the user&apos;s earlier choice, and &quot;the only way to turn them on now is in your phone&apos;s Settings&quot; is the honest mechanic, not a sales pitch. The share of declined users who open Settings from this sheet is meaningfully higher than the share who responded to the previous &quot;Notifications are off, enable them&quot; banner inside the app shell.&lt;/p&gt;

&lt;h2 id=&quot;the-measurement&quot;&gt;The measurement&lt;/h2&gt;

&lt;p&gt;We&apos;re tracking three numbers, weekly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our screen&apos;s tap rate.&lt;/strong&gt; What share of users who land on our screen tap &quot;Turn on notifications&quot;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission grant rate after the CTA.&lt;/strong&gt; Of the users who tap our CTA, how many then tap &quot;Allow&quot; on the system prompt that follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Net grant rate.&lt;/strong&gt; Our screen&apos;s tap rate times the system-prompt grant rate. The lift over the bare system-prompt baseline is the metric the product team watches, and on this product it&apos;s meaningful, meaningful enough that showing our own screen first is now the model for every new permission ask, not just notifications.&lt;/p&gt;

&lt;h2 id=&quot;what-i-would-tell-another-engineer-setting-this-up&quot;&gt;What I would tell another engineer setting this up&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Never call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestPermissionsAsync&lt;/code&gt; without checking the status first.&lt;/strong&gt; The one-shot rule on iOS is unforgiving, the three-branch guard is one of those patterns where the lines you don&apos;t call matter more than the ones you do.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Write the screen copy around concrete, positive jobs.&lt;/strong&gt; Habit reminders the user just set up, lab results the moment they&apos;re ready, milestones worth celebrating. Specific and rewarding beats &quot;stay engaged with the app&quot;, the same reason game notifications lead with wins, not obligations.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Let users skip your screen, never let them skip into the OS prompt.&lt;/strong&gt; A &quot;Skip for now&quot; on your own screen is cheap, the status stays undetermined and you can ask again at a better moment. A &quot;Don&apos;t Allow&quot; on the system prompt is permanent. Design the screen so the unsure user takes the cheap no.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Settings deep link for the declined cohort.&lt;/strong&gt; Most users who declined the first time won&apos;t come back, some will, so make the recovery a one-tap path.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ask for the iOS flags you actually need.&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allowAnnouncements&lt;/code&gt; isn&apos;t free, every permission you ask for raises the bar to &quot;yes&quot;.&lt;/li&gt;
&lt;/ol&gt;

&lt;aside class=&quot;post-cta&quot;&gt;
  &lt;h2 id=&quot;we-could-reshape-your-permission-flow&quot;&gt;We Could Reshape Your Permission Flow&lt;/h2&gt;

  &lt;p&gt;If your app is hitting the system permission prompt cold and a meaningful share of your onboarding cohort is declining, that&apos;s one sprint of work that pays for itself in feature reach. &lt;strong&gt;Clearview Team&lt;/strong&gt; has shipped this exact flow on a B2C app and the grant rate is trending well ahead of the bare-prompt baseline. We can design the screen copy, write the three-branch hook and wire the Settings recovery path on iOS and Android. Bring the product, we&apos;ll bring the flow.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;mailto:info@clearview.team?subject=Notification%20permission%20enquiry&quot;&gt;Brief us on your permission flow →&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;
</content>
    <category term="react-native" />
    <category term="mobile" />
    <category term="ios" />
    <category term="android" />
    <category term="ux" />
    <category term="notifications" />
    <category term="frontend-mobile" />
    <category term="case-study" />
    
  </entry>
  
</feed>
