Amba

Expo SDK

Expo SDK quickstart for @layers/amba-expo — config plugin, Apple/Google sign-in, push integration, and prebuild workflow. Builds on @layers/amba-react-native.

@layers/amba-expo is the Expo wrapper around @layers/amba-react-native. It re-exports the same Amba singleton, and adds an Expo config plugin that wires up the native config the SDK needs (push entitlements, URL schemes, Associated Domains, Android intent filters) at prebuild time.

Use this SDK if you're on the Expo managed workflow or building with EAS Build. For bare React Native apps without Expo modules, use @layers/amba-react-native directly.

1. Install

npx expo install @layers/amba-expo @react-native-async-storage/async-storage
npx expo install expo-notifications expo-device expo-apple-authentication expo-auth-session

Expo will pick the version of each peer that matches your SDK version.

2. Add the config plugin to app.json

{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "scheme": "myapp",
    "plugins": [
      [
        "@layers/amba-expo",
        {
          "apiKey": "amb_dev_ck_XXXX",
          "scheme": "myapp",
          "googleIosClientId": "YOUR_GOOGLE_OAUTH_IOS_CLIENT_ID"
        }
      ]
    ]
  }
}

The plugin accepts these options:

  • scheme — adds a CFBundleURLTypes entry on iOS so myapp://... deep-links route into the app, and an intent-filter on the Android main activity for the same scheme.
  • googleIosClientId — appends a reverse-DNS-ed CFBundleURLTypes entry so the Google sign-in redirect lands back in the app.
  • apiKey and baseUrl — accepted in the plugin options for symmetry with Amba.configure(...); runtime config is the canonical source.

For Universal Links / APS Environment / UIBackgroundModes / Google Web client wiring, configure them directly in app.json's ios.associatedDomains / ios.entitlements / android.permissions and the corresponding native config blocks.

Re-run npx expo prebuild --clean after changing plugin properties.

3. Configure the SDK at runtime

// App.tsx
import { useEffect } from 'react';
import { Amba } from '@layers/amba-expo';
import { RootNavigator } from './navigation';
 
export default function App() {
  useEffect(() => {
    void Amba.configure({
      apiKey: process.env.EXPO_PUBLIC_AMBA_API_KEY!,
    });
  }, []);
 
  return <RootNavigator />;
}

The plugin only handles native config — runtime initialization still happens via Amba.configure({ apiKey }). Keep EXPO_PUBLIC_AMBA_API_KEY=amb_dev_ck_XXXX in your env so the same value reaches both prebuild and runtime.

For EAS Build, set the same env in your eas.json:

{
  "build": {
    "production": {
      "env": {
        "EXPO_PUBLIC_AMBA_API_KEY": "amb_dev_ck_XXXX"
      }
    }
  }
}

4. Sign in with Apple

The plugin wires the entitlement; the runtime call uses expo-apple-authentication:

import * as AppleAuthentication from 'expo-apple-authentication';
import { Amba } from '@layers/amba-expo';
import { Platform } from 'react-native';
 
export function AppleButton() {
  if (Platform.OS !== 'ios') return null;
  return (
    <AppleAuthentication.AppleAuthenticationButton
      buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
      buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
      cornerRadius={8}
      style={{ width: '100%', height: 48 }}
      onPress={async () => {
        const credential = await AppleAuthentication.signInAsync({
          requestedScopes: [
            AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
            AppleAuthentication.AppleAuthenticationScope.EMAIL,
          ],
        });
        if (credential.identityToken) {
          await Amba.auth.signInWithSocial('apple', credential.identityToken);
        }
      }}
    />
  );
}

5. Sign in with Google

import { useEffect } from 'react';
import * as Google from 'expo-auth-session/providers/google';
import { Amba } from '@layers/amba-expo';
 
export function GoogleButton() {
  const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
    clientId: process.env.EXPO_PUBLIC_GOOGLE_OAUTH_WEB_CLIENT_ID!,
    iosClientId: process.env.EXPO_PUBLIC_GOOGLE_OAUTH_IOS_CLIENT_ID!,
    androidClientId: process.env.EXPO_PUBLIC_GOOGLE_OAUTH_ANDROID_CLIENT_ID!,
  });
 
  useEffect(() => {
    if (response?.type === 'success' && response.params.id_token) {
      void Amba.auth.signInWithSocial('google', response.params.id_token);
    }
  }, [response]);
 
  return <Button title="Sign in with Google" onPress={() => promptAsync()} disabled={!request} />;
}

The plugin's googleIosClientId and googleWebClientId properties keep the client IDs consistent between prebuild and runtime — set them once in app.json and reference them at runtime via env vars.

6. Push integration

import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import { Platform } from 'react-native';
import { Amba } from '@layers/amba-expo';
 
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});
 
export async function registerForPushNotificationsAsync() {
  if (!Device.isDevice) return null;
 
  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;
  if (existingStatus !== 'granted') {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }
  if (finalStatus !== 'granted') return null;
 
  const { data: token } = await Notifications.getDevicePushTokenAsync();
  const platform = Platform.OS === 'ios' ? 'apns' : 'fcm';
  await Amba.push.register(token, platform);
  return token;
}

Call this after a successful sign-in or after the user explicitly opts in.

APNs entitlement wiring isn't in the plugin today (see § 2). Until it lands, add the APS Environment entitlement and UIBackgroundModes: ["remote-notification"] to ios.entitlements / ios.infoPlist in app.json manually — expo prebuild will pick them up from the config rather than from the plugin.

The plugin maps your scheme (e.g. myapp) into both ios/Info.plist and android/app/src/main/AndroidManifest.xml. amba's deep-link API delivers links to your app:

import { useEffect } from 'react';
import * as Linking from 'expo-linking';
 
useEffect(() => {
  const sub = Linking.addEventListener('url', ({ url }) => {
    // url === 'myapp://post/123' or 'https://myapp.com/post/123'
  });
  return () => sub.remove();
}, []);

To list deep links registered with amba server-side, query the deep-links module via the client SDK reference.

EAS Build env

For EAS Build to receive the API key + Google OAuth IDs at build time:

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "env": {
        "EXPO_PUBLIC_AMBA_API_KEY": "amb_dev_ck_XXXX",
        "EXPO_PUBLIC_GOOGLE_OAUTH_IOS_CLIENT_ID": "..."
      }
    },
    "production": {
      "env": {
        "EXPO_PUBLIC_AMBA_API_KEY": "amb_live_ck_XXXX",
        "EXPO_PUBLIC_GOOGLE_OAUTH_IOS_CLIENT_ID": "..."
      }
    }
  }
}

Use the dev key for development builds and the production key for the production profile.

Common pitfalls

  • Forgot to re-prebuild after plugin changes — running npx expo prebuild --clean after editing app.json is mandatory. Otherwise the entitlements / Info.plist won't reflect the new config and push registration fails silently.
  • APNs key mismatch — the APNs auth key uploaded to amba (console → push) must match the bundle ID set in app.json's ios.bundleIdentifier. If they drift, push token registration returns 200 but server-side delivery 404s.
  • Google OAuth client IDs across environments — each EAS build profile needs its own client ID matching the bundle ID for that profile. Reusing the dev client ID in production builds fails with invalid_client.
  • Plugin pinned to wrong amba SDK version — when bumping @layers/amba-expo, run npx expo install --check to align peer versions, then re-prebuild.

See also

On this page