Skip to main content
The video uses a specific AI coding tool to demonstrate the setup, but the configuration works 1:1 with Cursor, Claude Code, or any other tool. Despia is web framework and tooling agnostic, so the only thing that matters is the SDK call.
settingsapp:// deep-links the user straight into your app’s permissions page in the iOS Settings app or the Android Settings app. From there they can flip notifications, location, camera, microphone, contacts, photos, or any other permission they previously denied. This is the only path to re-grant a permission after the first denial, since iOS and Android refuse to re-prompt within the app.
Useful for re-engaging users who declined push permission on first launch. iOS only allows the system push prompt to show once, but you can build any number of in-app prompts that link out to this settings page, letting users opt into notifications later through a UI you control.

Installation

npm install despia-native
import despia from 'despia-native';

How it works

settingsapp:// is fire-and-forget. The runtime hands the URL to the OS, which opens its native Settings app on your app’s permissions page. Your app moves to the background while the user is in Settings, and resumes where they left off when they switch back.
import despia from 'despia-native'

const isDespia = navigator.userAgent.toLowerCase().includes('despia')

if (isDespia) {
    despia('settingsapp://')
}
The user has to tap to come back to your app, the OS does not return them automatically when they finish toggling a setting. Plan your UI around that round trip rather than assuming the change is instant.

Re-prompt for push notifications

The most common use is recovering from a “Don’t Allow” tap on the iOS push prompt. Apple only shows the system prompt once per install, so any subsequent ask has to be a custom UI that links out to Settings. The pattern: detect that push is disabled, surface a friendly prompt, link to Settings on tap.
import { useEffect, useState } from 'react'
import despia from 'despia-native'

const isDespia = navigator.userAgent.toLowerCase().includes('despia')

function PushPrompt() {
    const [pushEnabled, setPushEnabled] = useState(true)

    useEffect(() => {
        if (!isDespia) return

        async function check() {
            const result = await despia('checkNativePushPermissions://', ['nativePushEnabled'])
            setPushEnabled(result.nativePushEnabled)
        }

        check()
    }, [])

    if (pushEnabled) return null

    return (
        <div className="banner">
            <p>Turn on notifications to never miss a message</p>
            <button onClick={() => despia('settingsapp://')}>
                Open Settings
            </button>
        </div>
    )
}
The checkNativePushPermissions:// scheme returns whether push is currently enabled, so you only show the banner when it would actually do something. See the OneSignal reference for the full check-then-prompt flow.

Re-prompt for any other permission

The same pattern works for every permission your app uses. Detect that the permission was denied, explain what the feature does in your own words, then link to Settings. This works for location, camera, microphone, contacts, photos, calendar, and any other permission iOS or Android exposes.
function CameraPermissionPrompt({ onSettingsOpened }) {
    function openSettings() {
        if (isDespia) {
            despia('settingsapp://')
            onSettingsOpened?.()
        }
    }

    return (
        <div>
            <p>Camera access is required to scan barcodes. Enable it in Settings to continue.</p>
            <button onClick={openSettings}>Open Settings</button>
        </div>
    )
}
Pair the call with an onSettingsOpened handler so you know to re-check the permission when the app returns to the foreground. The user might toggle the permission and come back, or they might not, and your UI should react to either outcome.

Re-check permissions when the app returns

Use the page visibility API to re-check permissions when the user returns from Settings. The browser fires visibilitychange when the WebView regains focus, which is exactly when you want to re-evaluate whether the permission was granted.
import despia from 'despia-native'

const isDespia = navigator.userAgent.toLowerCase().includes('despia')

function watchPermissionChanges(onChange) {
    if (!isDespia) return

    document.addEventListener('visibilitychange', async () => {
        if (document.visibilityState === 'visible') {
            const result = await despia('checkNativePushPermissions://', ['nativePushEnabled'])
            onChange(result.nativePushEnabled)
        }
    })
}
In React, attach the listener inside useEffect and return a cleanup function so it does not leak between mounts.

Resources

NPM Package

despia-native