---
url: /getting-started/compatibility.md
---
# Compatibility

`expo-apple-sign-in` runs on iOS, Android and web in Expo SDK 58 projects that use development builds or EAS Build. The table below shows which environments work and what each one needs, and the sections after it explain the SDK 58 limit and the browser behavior on web.

## Support table

| Environment | Supported | Notes |
| --- | --- | --- |
| Expo SDK 58 | Yes | The only SDK the library targets. |
| Expo SDK 57 and earlier | No | These SDKs lack the Expo Modules 2.0 API that the native code is written with. |
| Expo Go | No | Expo Go cannot load the library's native module, `ExpoAppleSignIn`. |
| Development builds | Yes | Build with `npx expo run:ios` or `npx expo run:android` after adding the config plugin. |
| EAS Build | Yes | EAS runs the config plugin when it generates the native projects. |
| iOS | Yes | iOS 16.4 or later, and an App ID with Sign in with Apple enabled. No `AppleAuth.configure` call is needed, and `AppleAuth.isConfigured()` always returns `true`. |
| Android | Yes | A Services ID as `clientId` and an HTTPS `redirectUri` registered on it. Sign-in runs in an in-app WebView screen. |
| Web | Yes | The same Services ID and a `redirectUri` on the same origin as the page. Sign-in runs in an Apple JS popup, which must open from a click or press handler. |

Some behavior differs by platform. Only iOS reports a credential state and revocation: on Android and web, `AppleAuth.getCredentialState` always returns `'unknown'`, and `AppleAuth.addRevokeListener` returns a subscription whose listener never runs. Only iOS fills `middleName`, `namePrefix`, `nameSuffix`, and `nickname`, and the other platforms return `null` for them. iOS and Android run one sign-in at a time, so a second `signIn` call while the first is open rejects with `ERR_REQUEST_FAILED`.

## Why only SDK 58

The native modules are written with the Expo Modules 2.0 API that ships with SDK 58, and earlier SDKs do not support it. The Swift and Kotlin modules declare their functions with `@ExpoModule` and `@JS`, the Android build file turns on Modules 2.0 with `expoModule { v2 true }`, and Android registers the module through the Modules 2.0 generated module list. The iOS errors use the SDK 58 `Exception(name:description:code:)` initializer, which is how codes such as `ERR_REQUEST_CANCELED` reach JavaScript. The library has no code path for the older module API, so it cannot fall back on an earlier SDK.

## Web browser notes

On web the library has no native module. It drives Apple's JavaScript SDK in the browser, and a few details of that path affect where and how it runs.

`AppleAuth.isAvailable()` returns `true` whenever the code runs with a browser `document`, and `false` where there is none, such as in Node. It does not check that Apple's script can load, so a `true` result does not guarantee that sign-in will start.

The first call to `AppleAuth.signIn()` adds a `<script>` tag for `https://appleid.apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js` to the page, or waits for that tag if the page already includes it. If the script fails to load, sign-in rejects with `ERR_NOT_AVAILABLE` and the message `Failed to load the Apple JS SDK.`, and the library removes the tag so the next call tries to load the script again. A site that sends a Content Security Policy must allow scripts from `https://appleid.apple.com`.

The library calls Apple JS with `usePopup: true`, so Apple's sign-in page opens in a popup window and the result returns to the page that started it. Browsers block a popup that no user gesture opened, so call `AppleAuth.signIn()` from a click or press handler.

When Apple JS rejects, the library maps its error code. `popup_closed_by_user`, `user_cancelled_authorize`, and any other code that contains `cancel` or `closed` become `ERR_REQUEST_CANCELED`, the same code a dismissed sheet produces on iOS. `popup_blocked_by_browser` becomes `ERR_REQUEST_FAILED`, and any other code becomes `ERR_REQUEST_UNKNOWN` with the message `Apple sign-in failed: <code>`.

Before it loads Apple JS, the library checks that `redirectUri` has the same origin as the page and rejects with `ERR_NOT_CONFIGURED` if it does not. [Web](/setup/web) explains the rule and how to register the matching Return URL.
