@safercity/sdk-react-native
v0.4.0
Published
React Native adapter for SaferCity SDK with expo-fetch streaming support
Maintainers
Readme
@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 useisPremiumflag. - 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 -
SaferCityProvidernow supportstenantId,userId, andheadersin 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
radiustodays.
// 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 -
SaferCityApiErrorwithstatus,error,messagefields 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
userIdinSaferCityProviderand client options. - New Hooks - Access to all new hooks from
@safercity/sdk-reactincluding Panic Information.
What's New in v0.1.3
- Version update - Updated to reflect v0.1.3 changes in core and react packages
Installation
npm install @safercity/sdk-react-native @safercity/sdk-react @tanstack/react-query
# or
bun add @safercity/sdk-react-native @safercity/sdk-react @tanstack/react-queryRequirements
- React Native >= 0.72.0
- React >= 18.0.0
- Expo SDK 52+ (recommended for streaming support)
Quick Start
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:
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:
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:
import {
SecureStoreTokenStorage,
AsyncStorageTokenStorage,
InMemoryTokenStorage,
} from '@safercity/sdk-react-native';
const storage = new SecureStoreTokenStorage();
await storage.waitForInit(); // wait for initial load from device storageStreaming Support
This package automatically uses expo-fetch for streaming when available (Expo SDK 52+).
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
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.
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:
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.
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
- Ensure you're using Expo SDK 52 or later
- Check that
expo-fetchis properly installed - Use
isStreamingSupported()to check availability - Fall back to polling if streaming is not supported
Type errors
Make sure you have the correct peer dependencies:
npm install react@^18.0.0 react-native@^0.72.0 @tanstack/react-query@^5.0.0License
MIT
