npm package: @safercity/sdk-react-native
Description: React Native adapter for SaferCity SDK with expo-fetch streaming support
Author: SaferCity
Latest version: 0.4.0

# @safercity/sdk-react-native

React Native adapter for SaferCity SDK with expo-fetch streaming support and secure token storage.

## What's New in v0.4.0

- **100% Codegen Types** - All types flow from the generated OpenAPI spec. No manual type definitions.
- **SDK Realignment** - Updated to reflect v0.4.0 client and React hook changes.
- **Breaking Changes** - `useSubscribeUser()` hook removed. Subscription creation body updated to use `isPremium` flag.
- **New Features** - Support for premium panic creation and enhanced location update metadata.

## What's New in v0.3.2

- **Proxy Mode for White-Label Apps** - `SaferCityProvider` now supports `tenantId`, `userId`, and `headers` in proxy mode. Essential for white-label React Native apps where the native app talks to your backend proxy, which then forwards to SaferCity API.
- **useBanner Fix** - Fixed hook parameter from `radius` to `days`.

```tsx
// White-label React Native app
<SaferCityProvider
  mode="proxy"
  proxyBaseUrl={`${env.EXPO_PUBLIC_SERVER_URL}/api/safercity`}
  tenantId={env.EXPO_PUBLIC_TENANT_ID}
>
  <App />
</SaferCityProvider>
```

## What's New in v0.3.1

- **Simplified Return Types** - Updated to reflect v0.3.1 client changes. Domain methods return API body directly. Hook data types auto-adapt via `@safercity/sdk-react`.
- **Typed Errors** - `SaferCityApiError` with `status`, `error`, `message` fields on all API failures.

## What's New in v0.3.0

- **Typed SDK Alignment** - Updated to support the v0.3.0 auto-generated types and field name changes.

## What's New in v0.2.0

- **SDK Overhaul Alignment** - Updated to reflect v0.2.0 changes in core, client, and react packages.
- **User Scoping** - Full support for `userId` in `SaferCityProvider` and client options.
- **New Hooks** - Access to all new hooks from `@safercity/sdk-react` including Panic Information.

## What's New in v0.1.3

- **Version update** - Updated to reflect v0.1.3 changes in core and react packages

## Installation

```bash
npm install @safercity/sdk-react-native @safercity/sdk-react @tanstack/react-query
# or
bun add @safercity/sdk-react-native @safercity/sdk-react @tanstack/react-query
```

## Requirements

- React Native >= 0.72.0
- React >= 18.0.0
- Expo SDK 52+ (recommended for streaming support)

## Quick Start

```tsx
import { createReactNativeClient } from '@safercity/sdk-react-native';
import { SaferCityProvider } from '@safercity/sdk-react';

const client = createReactNativeClient({
  baseUrl: 'https://api.safercity.com',
  token: userToken,
  tenantId: 'tenant-123',
});

function App() {
  return (
    <SaferCityProvider {...client.getConfig()}>
      <Navigation />
    </SaferCityProvider>
  );
}
```

## Authentication Modes

On mobile, **direct mode** is the most common pattern. Your app authenticates users with an external auth provider and passes the token to SaferCity:

```tsx
import { SaferCityProvider } from '@safercity/sdk-react';

function App() {
  return (
    <SaferCityProvider
      mode="direct"
      baseUrl="https://api.safercity.com"
      tenantId="tenant-123"
      getAccessToken={() => getTokenFromAuthProvider()}
    >
      <Navigation />
    </SaferCityProvider>
  );
}
```

**Proxy mode** also works if your mobile app communicates through a backend-for-frontend (BFF). Cookie mode is not typical for mobile apps.

## Secure Token Storage

For server-side OAuth flows on mobile (e.g., background token management), the SDK provides secure storage that auto-detects the best available option:

```typescript
import { createSecureTokenStorage, getStorageType } from '@safercity/sdk-react-native';
import { TokenManager } from '@safercity/sdk';

// Auto-detects: expo-secure-store > AsyncStorage > in-memory
const storage = createSecureTokenStorage();

const tokenManager = new TokenManager({
  credentials: {
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
  },
  storage,
});

// Get token (auto-refreshes if expired)
const token = await tokenManager.getToken();

// Check what storage is being used
const type = getStorageType(); // 'secure-store' | 'async-storage' | 'memory'
```

### Storage Priority

| Storage | Package | Security | Persistence |
|---------|---------|----------|-------------|
| `SecureStoreTokenStorage` | `expo-secure-store` | Encrypted (device keychain/keystore) | Yes |
| `AsyncStorageTokenStorage` | `@react-native-async-storage/async-storage` | Unencrypted | Yes |
| `InMemoryTokenStorage` | Built-in | N/A | No |

You can also use a specific storage class directly:

```typescript
import {
  SecureStoreTokenStorage,
  AsyncStorageTokenStorage,
  InMemoryTokenStorage,
} from '@safercity/sdk-react-native';

const storage = new SecureStoreTokenStorage();
await storage.waitForInit(); // wait for initial load from device storage
```

## Streaming Support

This package automatically uses `expo-fetch` for streaming when available (Expo SDK 52+).

```tsx
import { isStreamingSupported } from '@safercity/sdk-react-native';

if (isStreamingSupported()) {
  console.log('SSE streaming is available');
} else {
  console.log('Streaming not available, consider polling');
}
```

### Using Streaming

```tsx
import { usePanicStream } from '@safercity/sdk-react';
import { isStreamingSupported } from '@safercity/sdk-react-native';

function PanicTracker({ panicId }: { panicId: string }) {
  const { data, isConnected, error } = usePanicStream(panicId, {
    onEvent: (event) => {
      console.log('Panic update:', event.data);
    },
  });
  
  if (!isStreamingSupported()) {
    return <PollingPanicTracker panicId={panicId} />;
  }
  
  return (
    <View>
      <Text>Connected: {isConnected ? 'Yes' : 'No'}</Text>
      <Text>Latest: {data?.data}</Text>
    </View>
  );
}
```

## Without Expo

For bare React Native projects without Expo, streaming will fall back to standard fetch. SSE may have limited support depending on your React Native version.

```tsx
import { createReactNativeClient } from '@safercity/sdk-react-native';

const client = createReactNativeClient({
  baseUrl: 'https://api.safercity.com',
  token: userToken,
  customFetch: myCustomFetch,
});
```

## Using with React Hooks

All hooks from `@safercity/sdk-react` work seamlessly:

```tsx
import { useUser, useCreatePanic } from '@safercity/sdk-react';

function Profile({ userId }: { userId: string }) {
  const { data, isLoading, error } = useUser(userId);
  
  if (isLoading) return <ActivityIndicator />;
  if (error) return <Text>Error: {error.message}</Text>;
  
  return (
    <View>
      <Text>Name: {data?.data.firstName} {data?.data.lastName}</Text>
      <Text>Status: {data?.data.status}</Text>
    </View>
  );
}
```

## API Reference

### createReactNativeClient(options)

Creates a SaferCity client optimized for React Native.

```typescript
interface ReactNativeClientOptions {
  baseUrl: string;
  token?: string;
  tenantId?: string;
  timeout?: number;
  headers?: Record<string, string>;
  customFetch?: typeof fetch;
}
```

### isStreamingSupported()

Returns `true` if expo-fetch is available and streaming is supported.

### getReactNativeFetch(customFetch?)

Returns the best available fetch implementation for the current environment. Priority: expo-fetch > custom > global fetch.

### createReactNativeStreamAdapter(customFetch?)

Creates a `FetchStreamAdapter` using the best available fetch for the environment.

### createSecureTokenStorage()

Auto-detects and returns the best available token storage. Priority: expo-secure-store > AsyncStorage > in-memory.

### getStorageType()

Returns `'secure-store'` | `'async-storage'` | `'memory'` indicating what storage is available.

## Troubleshooting

### Streaming not working

1. Ensure you're using Expo SDK 52 or later
2. Check that `expo-fetch` is properly installed
3. Use `isStreamingSupported()` to check availability
4. Fall back to polling if streaming is not supported

### Type errors

Make sure you have the correct peer dependencies:

```bash
npm install react@^18.0.0 react-native@^0.72.0 @tanstack/react-query@^5.0.0
```

## License

MIT