From 6f29cd298956ef8ba3d51cf40869d89cb05d98cc Mon Sep 17 00:00:00 2001 From: Andras Szucs Date: Thu, 11 Jun 2026 07:16:45 +0200 Subject: [PATCH] docs(wallets): add steps to make iOS work --- ...thenticate-oauth-with-expo-web-browser.mdx | 2 +- .../react-native/domain-association.mdx | 115 +++++++++++++++--- .../wallets/react-native/export-wallet.mdx | 8 +- .../wallets/react-native/google-oauth.mdx | 46 ++++++- .../pages/wallets/react-native/magic-link.mdx | 18 ++- docs/pages/wallets/react-native/passkeys.mdx | 11 +- .../pages/wallets/react-native/quickstart.mdx | 30 ++++- 7 files changed, 194 insertions(+), 36 deletions(-) diff --git a/docs/pages/wallets/hooks/use-authenticate-oauth-with-expo-web-browser.mdx b/docs/pages/wallets/hooks/use-authenticate-oauth-with-expo-web-browser.mdx index 8a30833..6314078 100644 --- a/docs/pages/wallets/hooks/use-authenticate-oauth-with-expo-web-browser.mdx +++ b/docs/pages/wallets/hooks/use-authenticate-oauth-with-expo-web-browser.mdx @@ -49,7 +49,7 @@ function OAuthLogin() { `string` -**Required.** The URI the OAuth flow redirects back to after sign-in. Either a custom-scheme deep link (e.g. `Linking.createURL('oauth-callback')`) or an [App Link](/wallets/react-native/google-oauth#using-an-app-link-as-the-redirect) on your verified domain (e.g. `https:///oauth-callback`). The URL must be allowlisted on the [ZeroDev Dashboard](https://dashboard.zerodev.app/), and your app must have a route that catches it. +**Required.** The URI the OAuth flow redirects back to after sign-in. Either a custom-scheme deep link (e.g. `Linking.createURL('oauth-callback')`) or a [verified `https` link](/wallets/react-native/google-oauth#using-a-verified-https-link-as-the-redirect) on your domain (e.g. `https:///oauth-callback` — delivered as an App Link on Android and via the auth session's `https` callback on iOS 17.4+). The URL must be allowlisted on the [ZeroDev Dashboard](https://dashboard.zerodev.app/), and your app must have a route that catches it. ## Mutation Parameters diff --git a/docs/pages/wallets/react-native/domain-association.mdx b/docs/pages/wallets/react-native/domain-association.mdx index 03d99d3..095fac4 100644 --- a/docs/pages/wallets/react-native/domain-association.mdx +++ b/docs/pages/wallets/react-native/domain-association.mdx @@ -5,19 +5,22 @@ ::: :::warning[Example setup — for development only] -This guide continues from the Expo starter project set up in the [quickstart](/wallets/react-native/quickstart), and shows an example setup for development. The committed debug keystore and the throwaway Vercel deployment are not suitable for production — a production app signs with a securely managed release key (e.g. [EAS credentials](https://docs.expo.dev/app-signing/app-credentials/); with [Play App Signing](https://support.google.com/googleplay/android-developer/answer/9842756), `assetlinks.json` must list Google's app-signing certificate fingerprint, not your upload key) and serves `assetlinks.json` from your real product domain. +This guide continues from the Expo starter project set up in the [quickstart](/wallets/react-native/quickstart), and shows an example setup for development. The committed debug keystore and the throwaway Vercel deployment are not suitable for production — a production app signs with a securely managed release key (e.g. [EAS credentials](https://docs.expo.dev/app-signing/app-credentials/); with [Play App Signing](https://support.google.com/googleplay/android-developer/answer/9842756), `assetlinks.json` must list Google's app-signing certificate fingerprint, not your upload key) and serves the verification files from your real product domain. ::: -Some features require Android to verify that your app and your domain belong together: the installed APK's signing-cert SHA-256 must match what the domain publishes in `/.well-known/assetlinks.json`. ([Expo guide](https://docs.expo.dev/linking/android-app-links/)) +Some features require the OS to verify that your app and your domain belong together: + +- **Android** checks that the installed APK's signing-cert SHA-256 matches what the domain publishes in `/.well-known/assetlinks.json`. ([Expo guide](https://docs.expo.dev/linking/android-app-links/)) +- **iOS** checks that the app's Team ID + bundle identifier appear in the domain's `/.well-known/apple-app-site-association` (AASA) file, and that the app declares the domain in its **Associated Domains** entitlement. ([Expo guide](https://docs.expo.dev/linking/ios-universal-links/)) Set this up once, and it unlocks: - [Passkeys](/wallets/react-native/passkeys) — WebAuthn requires the `rpId` domain to vouch for your app. -- **App Links** — `https` links on your domain that open the app directly, used by [Magic Link](/wallets/react-native/magic-link) and optionally by the [OAuth redirect](/wallets/react-native/google-oauth#using-an-app-link-as-the-redirect). +- **Verified `https` redirects** — links on your domain that return into the app, used by [Magic Link](/wallets/react-native/magic-link) (App Links / Universal Links) and optionally by the [OAuth redirect](/wallets/react-native/google-oauth#using-a-verified-https-link-as-the-redirect). -That means: a stable signing keystore, a hosted `assetlinks.json`, and an `rpId` pointing at that domain. +That means: a stable Android signing keystore, the Associated Domains entitlement on iOS, two hosted verification files, and an `rpId` pointing at the domain that serves them. -## 1. Sign every build with the same keystore +## 1. Android: sign every build with the same keystore Android only trusts the association if the installed APK's signing cert matches the fingerprint your domain publishes. Two defaults get in the way: @@ -92,7 +95,7 @@ Register it in `app.json` under `plugins`: > The plugin takes effect when the native project is regenerated — run `npx expo prebuild --clean`, or it happens automatically on the next `npx expo run:android` if the `android/` directory doesn't exist yet. -## 2. Extract the SHA-256 fingerprint +### Extract the SHA-256 fingerprint ```sh keytool -list -v \ @@ -100,15 +103,48 @@ keytool -list -v \ -alias androiddebugkey -storepass android -keypass android ``` -Copy the line under `Certificate fingerprints` starting with `SHA256:`. +Copy the line under `Certificate fingerprints` starting with `SHA256:` — it goes into `assetlinks.json` below. + +## 2. iOS: add the Associated Domains entitlement + +:::warning[Paid Apple Developer account required] +Associated Domains is a paid-tier entitlement — it only works with a paid [Apple Developer Program](https://developer.apple.com/programs/) membership. With a free personal team the app still builds and installs, but the entitlement is silently dropped from the provisioning profile, so the AASA file is never fetched and passkeys / Universal Links fail with opaque errors. +::: + +Grab your **Team ID** from the [Apple Developer membership page](https://developer.apple.com/account), then declare it and the domain in `app.json`: + +```jsonc +{ + "expo": { + "ios": { + "bundleIdentifier": "", + "appleTeamId": "", // [!code ++] + "associatedDomains": [ // [!code ++] + "webcredentials:", // [!code ++] + "applinks:?mode=developer" // [!code ++] + ] // [!code ++] + }, + }, +} +``` + +- `webcredentials:` is the entry [passkeys](/wallets/react-native/passkeys) check; `applinks:` is the one Universal Links ([Magic Link](/wallets/react-native/magic-link)) check. +- `?mode=developer` makes development builds fetch the AASA file directly from your origin instead of Apple's CDN, which can cache a stale copy for up to ~24h after you deploy. App Store builds strip the flag, so production traffic still goes through the CDN. +- `appleTeamId` sets the development team for code signing in the generated Xcode project, so `npx expo run:ios` can sign without opening Xcode. + +> Associated Domains is a **build-time** entitlement, not runtime config. After adding or changing an entry, regenerate the native project and rebuild — `npx expo prebuild --clean`, then `npx expo run:ios`. Re-running against an already-built binary won't pick it up. -## 3. Create and host `assetlinks.json` +## 3. Create and host the verification files + +Create a folder for the two `/.well-known/` files: ```sh -mkdir -p assetlinks/public/.well-known && touch ./assetlinks/public/.well-known/assetlinks.json +mkdir -p assetlinks/public/.well-known ``` -Paste this in, with your package name (from `app.json` → `android.package`) and the fingerprint from the previous step: +### `assetlinks.json` (Android) + +Create `assetlinks/public/.well-known/assetlinks.json` with your package name (from `app.json` → `android.package`) and the SHA-256 fingerprint extracted in step 1: ```json [ @@ -126,7 +162,44 @@ Paste this in, with your package name (from `app.json` → `android.package`) an ] ``` -Host it on Vercel: +### `apple-app-site-association` (iOS) + +Create `assetlinks/public/.well-known/apple-app-site-association` (no file extension) with your Team ID and bundle identifier: + +```json +{ + "applinks": { + "details": [ + { + "appIDs": ["."], + "components": [{ "/": "/verify-email*" }] + } + ] + }, + "webcredentials": { + "apps": ["."] + } +} +``` + +- `webcredentials` is what passkeys check. +- `applinks.details[].components` lists the `https` paths that should open your app — `/verify-email*` is the one the [Magic Link](/wallets/react-native/magic-link) guide uses (the trailing `*` also matches the `?code=...` query string). Don't claim paths your app doesn't handle: every Safari navigation to a claimed URL gets intercepted by your app. + +Apple requires the extension-less AASA file to be served as JSON, so pin its `Content-Type` with an `assetlinks/vercel.json`: + +```json +{ + "outputDirectory": "public", + "headers": [ + { + "source": "/.well-known/apple-app-site-association", + "headers": [{ "key": "Content-Type", "value": "application/json" }] + } + ] +} +``` + +### Host on Vercel :::code-group @@ -152,13 +225,23 @@ bunx vercel ::: -Then check `https://.vercel.app/.well-known/assetlinks.json` to confirm it deployed correctly. +Then verify both files deployed correctly: + +```sh +curl -i https://.vercel.app/.well-known/assetlinks.json +curl -i https://.vercel.app/.well-known/apple-app-site-association +# expect 200 + application/json for both, with no redirects +``` + +iOS devices don't fetch the AASA from your origin — they go through **Apple's CDN** (unless the `?mode=developer` flag from step 2 is active). Check what the CDN sees: + +```sh +curl https://app-site-association.cdn-apple.com/a/v1/.vercel.app +``` + +If the CDN payload is stale after a deploy, development builds with `?mode=developer` bypass it; alternatively, toggle **Settings → Developer → Universal Links → Associated Domains Development** on the test device (the Developer menu appears once the device has been connected to Xcode). ## 4. Point the SDK at the domain - Change `RP_ID` in `wagmi.config.ts` to the deployed domain (no scheme): `.vercel.app`. - If you specify an Access Control List of whitelisted Origins on the [ZeroDev Dashboard](https://dashboard.zerodev.app/), add `https://.vercel.app/` to the allowlist. - -:::info[On iOS] -The same association goes through `ios.associatedDomains` (`webcredentials:` for passkeys, `applinks:` for Universal Links), and that entitlement only works with a paid [Apple Developer](https://developer.apple.com/programs/) membership — see Expo's [iOS Universal Links guide](https://docs.expo.dev/linking/ios-universal-links/). -::: diff --git a/docs/pages/wallets/react-native/export-wallet.mdx b/docs/pages/wallets/react-native/export-wallet.mdx index 26a2711..5c2d69d 100644 --- a/docs/pages/wallets/react-native/export-wallet.mdx +++ b/docs/pages/wallets/react-native/export-wallet.mdx @@ -20,22 +20,22 @@ It needs `react-native-webview` and `uuid` (native module — rebuild the dev cl ```bash [npm] npx expo install react-native-webview uuid -npx expo run:android +npx expo run:android # or: npx expo run:ios ``` ```bash [yarn] yarn expo install react-native-webview uuid -yarn expo run:android +yarn expo run:android # or: yarn expo run:ios ``` ```bash [pnpm] pnpm expo install react-native-webview uuid -pnpm expo run:android +pnpm expo run:android # or: pnpm expo run:ios ``` ```bash [bun] bunx expo install react-native-webview uuid -bunx expo run:android +bunx expo run:android # or: bunx expo run:ios ``` ::: diff --git a/docs/pages/wallets/react-native/google-oauth.mdx b/docs/pages/wallets/react-native/google-oauth.mdx index 922a7ed..80119ec 100644 --- a/docs/pages/wallets/react-native/google-oauth.mdx +++ b/docs/pages/wallets/react-native/google-oauth.mdx @@ -4,7 +4,7 @@ **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. ::: -On the web, [Google OAuth](/wallets/auth/google-oauth) uses a popup. Native apps don't have popups — instead, the SDK opens the system auth browser and returns to your app via a deep link. The [`useAuthenticateOAuthWithExpoWebBrowser`](/wallets/hooks/use-authenticate-oauth-with-expo-web-browser) hook handles both ends of that round trip. +On the web, [Google OAuth](/wallets/auth/google-oauth) uses a popup. Native apps don't have popups — instead, the SDK opens the system auth browser and returns to your app via a deep link. The [`useAuthenticateOAuthWithExpoWebBrowser`](/wallets/hooks/use-authenticate-oauth-with-expo-web-browser) hook handles both ends of that round trip on both platforms: the auth session (`ASWebAuthenticationSession` on iOS, Custom Tabs on Android) intercepts the custom-scheme redirect itself, so no domain setup is needed. :::info This flow requires an [Expo development build](https://docs.expo.dev/develop/development-builds/expo-go-to-dev-build/) — see the [quickstart](/wallets/react-native/quickstart#5-switch-to-an-expo-development-build). @@ -106,9 +106,16 @@ This route is for the **native** OAuth flow. If the same Expo app also runs on w On the [ZeroDev Dashboard](https://dashboard.zerodev.app/), **allowlist the redirect URL**. After a successful sign-in the browser redirects back into the app. -## Using an App Link as the redirect +## Using a verified `https` link as the redirect -Once the [Domain Association](/wallets/react-native/domain-association) is set up on your domain, you can use an `https` **App Link** instead of the custom scheme — `https:///...` links then open the app directly ([Expo guide](https://docs.expo.dev/linking/android-app-links/)). +Once the [Domain Association](/wallets/react-native/domain-association) is set up on your domain, you can use a verified `https` link (`https:///oauth-callback`) instead of the custom scheme. + +The two platforms deliver this redirect through different mechanisms: + +- **Android** delivers it as a verified **App Link**: the OS hands the redirect to your app as an intent ([Expo guide](https://docs.expo.dev/linking/android-app-links/)). Requires an intent filter (below). +- **iOS 17.4+** resolves it inside the auth session itself: `ASWebAuthenticationSession` observes the redirect to your domain and returns it to the SDK. This is *not* Universal Link routing — iOS only opens Universal Links from a user tap, never from the server-side redirect that ends an OAuth flow — so the AASA doesn't need to claim the `/oauth-callback` path. What it does require is the `webcredentials:` entitlement (the same one passkeys use) from the [Domain Association](/wallets/react-native/domain-association#2-ios-add-the-associated-domains-entitlement) setup. + +### Android: add an intent filter Add an `intentFilter` for `/oauth-callback` in `app.json`: @@ -137,7 +144,15 @@ Add an `intentFilter` for `/oauth-callback` in `app.json`: > After `app.json` changes, regenerate the native project: `npx expo prebuild --clean`, then rebuild. Intent filters land in `AndroidManifest.xml` and App Link verification happens at install time. -Then use the App Link as the redirect: +### iOS: check the requirements + +No OAuth-specific setup beyond the [Domain Association](/wallets/react-native/domain-association) — but three things must hold: + +- The build carries the `webcredentials:` entitlement (paid Apple Developer team). Entitlement changes only land after `npx expo prebuild --clean` and a rebuild. +- The hosted `apple-app-site-association` lists your `.` under `webcredentials`. If the entitlement or AASA is missing or stale, the flow strands the user on the redirect page inside the auth sheet — during development, keep the `?mode=developer` flag on the entitlement so the device fetches the AASA straight from your origin instead of Apple's CDN. +- The device runs **iOS 17.4 or later**. Older versions can't observe `https` callbacks in the auth session — fall back to the custom scheme there (snippet below). + +### Pass the `https` redirect ```tsx import { RP_ID } from "@/wagmi.config"; @@ -147,4 +162,25 @@ const auth = useAuthenticateOAuthWithExpoWebBrowser({ }); ``` -Since this is a new redirect URL, remember to [allowlist it on the Dashboard](#5-allowlist-the-redirect-url) as well. +The hook detects the `https` redirect and configures the auth session accordingly — no extra option needed on either platform. + +To keep older iOS versions working, pick the redirect per platform and version: + +```tsx +import * as Linking from "expo-linking"; +import { Platform } from "react-native"; + +import { RP_ID } from "@/wagmi.config"; + +// iOS can only observe https auth-session callbacks on 17.4+. +const supportsHttpsRedirect = + Platform.OS !== "ios" || Number.parseFloat(String(Platform.Version)) >= 17.4; + +const auth = useAuthenticateOAuthWithExpoWebBrowser({ + redirectUri: supportsHttpsRedirect + ? `https://${RP_ID}/oauth-callback` + : Linking.createURL("oauth-callback"), +}); +``` + +Since these are new redirect URLs, remember to [allowlist them on the Dashboard](#5-allowlist-the-redirect-url) as well. diff --git a/docs/pages/wallets/react-native/magic-link.mdx b/docs/pages/wallets/react-native/magic-link.mdx index 3422431..cac0a31 100644 --- a/docs/pages/wallets/react-native/magic-link.mdx +++ b/docs/pages/wallets/react-native/magic-link.mdx @@ -6,16 +6,18 @@ The [`useSendMagicLink`](/wallets/hooks/use-send-magic-link) and [`useVerifyMagicLink`](/wallets/hooks/use-verify-magic-link) hooks work identically to [the web](/wallets/auth/magic-link) on React Native, but the redirect needs platform setup. -A magic link needs an **App Link** callback so that tapping the link in the email opens the app — email clients (e.g. Gmail) don't render custom-scheme links, so a plain `https` link on your verified domain is required. +A magic link needs a verified `https` callback — an **App Link** on Android, a **Universal Link** on iOS — so that tapping the link in the email opens the app. Email clients (e.g. Gmail) don't render custom-scheme links, so a plain `https` link on your verified domain is required. The flow: send → the backend emails `${redirectURL}?code=` → the user taps it → the app opens at `/verify-email` → the route pairs the `code` from the URL with the persisted `otpId`/`otpEncryptionTargetBundle` and verifies. ## Prerequisites -- Complete the [Domain Association](/wallets/react-native/domain-association) setup — App Links only work on a domain that serves your `assetlinks.json`. +- Complete the [Domain Association](/wallets/react-native/domain-association) setup — App Links and Universal Links only work on a domain that serves your verification files (`assetlinks.json` on Android, `apple-app-site-association` on iOS). - Use an [Expo development build](https://docs.expo.dev/develop/development-builds/expo-go-to-dev-build/) — see the [quickstart](/wallets/react-native/quickstart#5-switch-to-an-expo-development-build). -## Add an App Link for `/verify-email` +## Set up the `/verify-email` link + +### Android: add an intent filter With the domain association in place, `https:///...` links can open the app directly ([Expo guide](https://docs.expo.dev/linking/android-app-links/)). Each path your app should catch needs its own filter in `app.json` — add one for `/verify-email`: @@ -44,6 +46,16 @@ With the domain association in place, `https:///...` links can open > After `app.json` changes, regenerate the native project: `npx expo prebuild --clean`, then rebuild. Intent filters land in `AndroidManifest.xml` and App Link verification happens at install time. +### iOS: the Universal Link is already claimed + +On iOS there is no per-path entry in `app.json` — the `applinks:` entitlement plus the AASA's `components` rule (`{ "/": "/verify-email*" }`) from the [Domain Association](/wallets/react-native/domain-association) setup already claim the path. Keep in mind: + +- If you change which paths the AASA claims, redeploy it; entitlement changes additionally need `npx expo prebuild --clean` and a rebuild. +- iOS only triggers Universal Links from a **tap in another app** (e.g. Mail) — typing the URL into Safari's address bar opens the website, not the app. +- Apple's CDN caches the AASA; during development use the `?mode=developer` flag (see [Domain Association](/wallets/react-native/domain-association#2-ios-add-the-associated-domains-entitlement)) so the device fetches it straight from your origin. + +### Allowlist the redirect URL + On the [ZeroDev Dashboard](https://dashboard.zerodev.app/), add `https:///verify-email` as an allowlisted **Magic Link Redirect URL**. ## Magic link flow diff --git a/docs/pages/wallets/react-native/passkeys.mdx b/docs/pages/wallets/react-native/passkeys.mdx index 516b2bd..35e6db1 100644 --- a/docs/pages/wallets/react-native/passkeys.mdx +++ b/docs/pages/wallets/react-native/passkeys.mdx @@ -1,4 +1,4 @@ -# Passkeys on React Native [Native WebAuthn on Android] +# Passkeys on React Native [Native WebAuthn on Android & iOS] :::danger[IMPORTANT] **USE FOR INTERNAL TESTING PURPOSES ONLY.** You may use these features solely for internal evaluation purposes on supported testnets. DO NOT use for production use or share with your users. Wallets created during this preview ("Alpha Wallets") will be discontinued. Any tokens remaining within Alpha Wallets will be permanently lost upon discontinuance. Any mainnet tokens sent to an Alpha Wallet will not be deposited and will be permanently lost when discontinued. We are unable to help recover any lost funds from Alpha Wallets. We provide all previews on an "as is" basis without warranty of any kind, and we may terminate or suspend the availability of any preview at any time. @@ -6,11 +6,11 @@ The [`useRegisterPasskey`](/wallets/hooks/use-register-passkey) and [`useLoginPasskey`](/wallets/hooks/use-login-passkey) hooks work identically to [the web](/wallets/auth/passkeys) on React Native, but they require a native passkey stamper and some platform setup. -Passkeys (WebAuthn) require the installed APK's signing-cert SHA-256 to match what your domain publishes in `/.well-known/assetlinks.json` — that's the [Domain Association](/wallets/react-native/domain-association) setup. +Passkeys (WebAuthn) require the OS to verify that your app and your `rpId` domain belong together. On Android, the installed APK's signing-cert SHA-256 must match what the domain publishes in `/.well-known/assetlinks.json`; on iOS, the app's Team ID + bundle identifier must appear in the domain's `/.well-known/apple-app-site-association` and the app must carry the matching `webcredentials:` entitlement. Both are covered by the [Domain Association](/wallets/react-native/domain-association) setup. ## Prerequisites -- Complete the [Domain Association](/wallets/react-native/domain-association) setup: `RP_ID` points at your domain, `assetlinks.json` is served from it, and the domain is on the Dashboard's Origin allowlist if you use one. +- Complete the [Domain Association](/wallets/react-native/domain-association) setup: `RP_ID` points at your domain, the verification files (`assetlinks.json` and `apple-app-site-association`) are served from it, and the domain is on the Dashboard's Origin allowlist if you use one. On iOS this includes the Associated Domains entitlement, which needs a paid [Apple Developer](https://developer.apple.com/programs/) membership. - Use an [Expo development build](https://docs.expo.dev/develop/development-builds/expo-go-to-dev-build/) — see the [quickstart](/wallets/react-native/quickstart#5-switch-to-an-expo-development-build). ## 1. Add the passkey stamper @@ -114,6 +114,9 @@ export function PasskeyFlow() { } ``` -The OS handles the passkey UI (biometric/PIN); the hooks auto-connect the wallet on success. The device needs Google Play services and an enrolled screen lock. +The OS handles the passkey UI (biometric/PIN); the hooks auto-connect the wallet on success. + +- **Android** needs Google Play services and an enrolled screen lock. +- **iOS** needs a device passcode (plus Face ID / Touch ID where available) — test on a physical device. An opaque *"The operation couldn't be completed"* error almost always means the AASA file and the app's entitlement don't match: re-run the verification checks in [Domain Association](/wallets/react-native/domain-association#3-create-and-host-the-verification-files), and remember that entitlement changes only land after `npx expo prebuild --clean` and a fresh build. If the same Expo app also runs on web, keep this component shared. The [React Native Web guide](/wallets/react-native/web) uses the same `PasskeyFlow` and lets the web build auto-default its WebAuthn stamper, so you do not need a separate `.web` variant for the passkey UI. diff --git a/docs/pages/wallets/react-native/quickstart.mdx b/docs/pages/wallets/react-native/quickstart.mdx index e5b72a5..761c3ad 100644 --- a/docs/pages/wallets/react-native/quickstart.mdx +++ b/docs/pages/wallets/react-native/quickstart.mdx @@ -367,7 +367,7 @@ bunx expo install expo-dev-client ::: -Build and run on Android: +Build and run on Android ([environment setup](https://docs.expo.dev/get-started/set-up-your-environment/?mode=development-build&buildEnv=local&platform=android&device=simulated)): :::code-group @@ -389,8 +389,32 @@ bunx expo run:android ::: -:::info[Why Android?] -These guides demonstrate the native flows on Android because a debug build runs on a device with no paid developer account. On iOS, the same [domain association](/wallets/react-native/domain-association) is required for passkeys and Universal Links (the `webcredentials:` and `applinks:` entries in `ios.associatedDomains`), and that entitlement only works with a paid [Apple Developer](https://developer.apple.com/programs/) membership. The flows themselves are identical — see Expo's [iOS Universal Links guide](https://docs.expo.dev/linking/ios-universal-links/) for the iOS setup. +Or on iOS — requires a Mac with Xcode ([environment setup](https://docs.expo.dev/get-started/set-up-your-environment/?mode=development-build&buildEnv=local&platform=ios&device=simulated)): + +:::code-group + +```bash [npm] +npx expo run:ios +``` + +```bash [yarn] +yarn expo run:ios +``` + +```bash [pnpm] +pnpm expo run:ios +``` + +```bash [bun] +bunx expo run:ios +``` + +::: + +The first run generates the native project (`expo prebuild`), builds it, installs the dev client on the emulator / Simulator, and starts the bundler. If `app.json` doesn't define `android.package` / `ios.bundleIdentifier` yet, the CLI prompts for one. + +:::info[What runs where] +Email OTP, [Google OAuth](/wallets/react-native/google-oauth), and [wallet export](/wallets/react-native/export-wallet) work on both platforms with no further setup — on iOS they run in the Simulator with a free Apple account. [Passkeys](/wallets/react-native/passkeys) and verified `https` links ([Magic Link](/wallets/react-native/magic-link)) additionally require the [domain association](/wallets/react-native/domain-association) setup: free on Android, while the iOS half (the Associated Domains entitlement) needs a paid [Apple Developer](https://developer.apple.com/programs/) membership and is best tested on a physical device. ::: ## Next Steps