One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Opening URLs and Settings

Hand a link to the app that owns it, show the share sheet, or open your app's settings page

Three functions hand work to the system and let it take over: One.openURL opens a link in whatever app handles it, One.openShare shows the share sheet, and One.openSettings opens your app’s page in Settings. On iOS and Android they use React Native’s Linking and Share modules; on web they use a new tab and the browser’s share sheet.

Use openURL to leave your app: a website in the browser, a phone number, an email, a map, or another app’s scheme. To show a page without leaving, use Browser instead.

Usage

import { Button, View } from 'react-native'
import { One } from 'one'
function ContactRow({ phone, email }: { phone: string; email: string }) {
return (
<View style={{ flexDirection: 'row', gap: 8 }}>
<Button title="Call" onPress={() => One.openURL(`tel:${phone}`)} />
<Button title="Email" onPress={() => One.openURL(`mailto:${email}`)} />
<Button
title="Share"
onPress={() => One.openShare({ message: `Reach me at ${email}` })}
/>
</View>
)
}

All three resolve with nothing, so the same code runs on every platform. Await them to catch the cases they reject.

How it works

Opening a URL on iOS. The URL goes to UIApplication.open, and iOS picks the app: Safari for https, Phone for tel:, Mail for mailto:, and any app that registered the scheme. An https link to a site with an installed app’s universal link opens that app. The promise rejects when no app takes the URL.

Opening a URL on Android. The URL becomes a view intent, with its scheme lowercased, started from your activity. Android picks the app, or asks people to choose when several can open it. Starting fails, and the promise rejects, when nothing installed handles it.

Sharing. On iOS, openShare shows the activity sheet with the message and the URL as separate items, so apps that take links get a real link, and it resolves once the sheet closes. Android’s share intent carries one string, so the message and URL are joined with a space; title becomes the subject, which email apps use. Android resolves as soon as the chooser opens.

Settings. openSettings opens your app’s own page in Settings on iOS and its app info screen on Android. That is where people turn on a permission they denied earlier, once the system has stopped showing its prompt.

Web. openURL calls window.open with a new tab and no opener, and resolves. openShare calls navigator.share with the title, message as text, and URL. openSettings rejects: the web has no app settings page.

API reference

FunctionResolves
openURL(url)Once the system takes the URL
openShare(content)Once the sheet is shown or closed
openSettings()Once Settings opens
OpenShareContentTypeDescription
titlestringThe subject on Android and the share title on web. Ignored on iOS.
stringThe subject on Android and the share title on web. Ignored on iOS.
messagestringThe text to share.
stringThe text to share.
urlstringA link to share. On Android it is added to the end of the message.
stringA link to share. On Android it is added to the end of the message.

Platform differences

iOSAndroidWeb
URL with no appRejectsRejectsOpens a tab
Share resolvesWhen closedWhen shownWhen done
Share cancelResolvesNot knownRejects
URL in shareIts own itemIn the textIts own field
Settings pageYesApp infoRejects

Limits and gotchas

Examples

import { One } from 'one'
async function openInMaps(address: string) {
const query = encodeURIComponent(address)
const native = One.platform === 'ios' ? `maps://?q=${query}` : `geo:0,0?q=${query}`
try {
await One.openURL(One.platform === 'web' ? `https://maps.google.com/?q=${query}` : native)
} catch {
await One.openURL(`https://maps.google.com/?q=${query}`)
}
}

The platform’s maps app opens when there is one. If it rejects, the web map opens in the browser instead, which every device has.

Share a post with a copy fallback on web

import { One } from 'one'
async function sharePost(title: string, url: string) {
try {
await One.openShare({ title, message: title, url })
} catch (error) {
if (One.platform === 'web' && (error as Error).name !== 'AbortError') {
await One.Clipboard.setString(url)
}
}
}

A browser without a share sheet copies the link instead. The check skips the copy when people simply cancelled, which browsers report as an AbortError.

Send people to Settings after a denial

import { Alert } from 'react-native'
import { One } from 'one'
function explainDeniedPermission(feature: string) {
Alert.alert(`${feature} is off`, `Turn on ${feature} in Settings to use this.`, [
{ text: 'Not now', style: 'cancel' },
{ text: 'Open Settings', onPress: () => One.openSettings() },
])
}

Once the system stops showing a permission prompt, asking again does nothing, so the alert explains why and takes them to the one place they can change it.

Edit this page on GitHub.