Advanced Usage
Advanced patterns and configurations for the Teardown SDK.
Multiple Environments
Use the environment_slug prop to target different environments within a single project:
const getEnvironment = () => __DEV__ ? 'development' : 'production';
export const teardown = new TeardownCore({
org_id: 'your-org-id',
project_id: 'your-project-id',
api_key: 'your-api-key',
environment_slug: getEnvironment(),
storageAdapter: new MMKVStorageAdapter(),
deviceAdapter: new ExpoDeviceAdapter(),
});Environments are configured in your project's dashboard settings. Common slugs include development, staging, and production (default).
Push Notifications Setup
Enable push notifications by providing a notification adapter:
import { TeardownCore } from '@teardown/force-updates';
import { ExpoNotificationsAdapter, ExpoDeviceAdapter } from '@teardown/force-updates/expo';
import { MMKVStorageAdapter } from '@teardown/force-updates/adapters/mmkv';
const teardown = new TeardownCore({
org_id: 'your-org-id',
project_id: 'your-project-id',
api_key: 'your-api-key',
storageAdapter: new MMKVStorageAdapter(),
deviceAdapter: new ExpoDeviceAdapter(),
notificationAdapter: new ExpoNotificationsAdapter(), // Enable notifications
});
// Request permissions and get token
if (teardown.notifications) {
const status = await teardown.notifications.requestPermissions();
if (status.granted) {
const token = await teardown.notifications.getToken();
// Token is automatically sent with identify() calls
}
}Handle Notifications
const { core } = useTeardown();
useEffect(() => {
if (!core.notifications) return;
const unsubs = [
core.notifications.onNotificationReceived((notification) => {
// Foreground notification
console.log('Notification:', notification.title);
}),
core.notifications.onNotificationOpened((notification) => {
// User tapped notification
navigateToScreen(notification.data?.screen);
}),
core.notifications.onDataMessage((message) => {
// Silent/data message
syncData(message.data);
}),
];
return () => unsubs.forEach(unsub => unsub());
}, [core.notifications]);Event Tracking
Track custom events:
const { core } = useTeardown();
// Track single event
await core.events.track({
event_name: 'purchase_completed',
event_type: 'action',
properties: {
product_id: 'abc123',
amount: 99.99,
currency: 'USD',
},
});
// Track multiple events
await core.events.trackBatch([
{ event_name: 'cart_viewed', event_type: 'screen_view' },
{ event_name: 'item_added', properties: { item_id: 'xyz' } },
]);Disable Force Update Checking
For debugging or testing:
const teardown = new TeardownCore({
// ...
forceUpdate: {
checkIntervalMs: -1, // Disable automatic checking
},
});Custom Version Check Interval
Check more or less frequently:
const teardown = new TeardownCore({
// ...
forceUpdate: {
checkIntervalMs: 60_000, // Check every minute
checkOnForeground: true,
},
});Manual Version Checking
Trigger version check programmatically:
const { core } = useTeardown();
const checkForUpdates = async () => {
const result = await core.identity.refresh();
if (result.success) {
// Version status will update via subscription
console.log('Version checked');
}
};Offline Support
The SDK handles offline scenarios gracefully:
- Cached session - Identity state persists across restarts
- Cached version status - Last known status is preserved
- Retry on foreground - Re-checks when connectivity restored
const session = useSession();
// Works offline - returns cached session
if (session) {
console.log('User:', session.user_id);
}Pre-warming
Initialize SDK before app is fully loaded:
// In app entry point (before React renders)
import { teardown } from './lib/teardown';
// SDK starts initializing immediately
// By the time UI renders, it may already be readyError Boundaries
Handle SDK errors gracefully:
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
<ErrorBoundary fallback={<ErrorScreen />}>
<TeardownProvider core={teardown}>
<MainApp />
</TeardownProvider>
</ErrorBoundary>
);
}Testing
Mock the SDK
// __mocks__/@teardown/force-updates.ts
export const TeardownProvider = ({ children }) => children;
export const useTeardown = () => ({
core: {
identity: {
identify: jest.fn().mockResolvedValue({ success: true, data: mockSession }),
signOut: jest.fn().mockResolvedValue({ success: true, data: undefined }),
signOutAll: jest.fn().mockResolvedValue({ success: true, data: undefined }),
getSessionState: () => mockSession,
},
forceUpdate: {
getVersionStatus: () => ({ type: 'up_to_date' }),
},
events: {
track: jest.fn().mockResolvedValue({ success: true, data: undefined }),
trackBatch: jest.fn().mockResolvedValue({ success: true, data: undefined }),
},
notifications: {
requestPermissions: jest.fn().mockResolvedValue({ granted: true, canAskAgain: true }),
getToken: jest.fn().mockResolvedValue('mock-push-token'),
onNotificationReceived: jest.fn().mockReturnValue(() => {}),
onNotificationOpened: jest.fn().mockReturnValue(() => {}),
onDataMessage: jest.fn().mockReturnValue(() => {}),
},
},
});
export const useSession = () => mockSession;
export const useForceUpdate = () => ({
versionStatus: { type: 'up_to_date' },
isUpdateRequired: false,
isUpdateRecommended: false,
isUpdateAvailable: false,
releaseNotes: null,
});Test Components
import { render } from '@testing-library/react-native';
test('shows login when not identified', () => {
jest.spyOn(hooks, 'useSession').mockReturnValue(null);
const { getByText } = render(<App />);
expect(getByText('Login')).toBeTruthy();
});Debugging
Enable Verbose Logging
teardown.setLogLevel('verbose');Inspect State
const { core } = useTeardown();
console.log('Identity state:', core.identity.getIdentifyState());
console.log('Version status:', core.forceUpdate.getVersionStatus());
console.log('Device ID:', await core.device.getDeviceId());Clear All Data
For debugging or logout:
// Sign out - clears session, keeps device ID
await core.identity.signOut();
// Full reset - clears session AND device ID (fresh install state)
await core.identity.signOutAll();Performance
Minimize Re-renders
Use specific hooks instead of broad subscriptions:
// Good - only re-renders on session change
const session = useSession();
// Avoid - accessing core gives no reactivity
const { core } = useTeardown();
const session = core.identity.getSessionState(); // Not reactive!Lazy Initialization
Defer SDK init until needed:
let teardownInstance: TeardownCore | null = null;
export const getTeardown = () => {
if (!teardownInstance) {
teardownInstance = new TeardownCore({ /* ... */ });
}
return teardownInstance;
};Cleanup
Properly cleanup on app shutdown:
import { AppState } from 'react-native';
useEffect(() => {
const subscription = AppState.addEventListener('change', (state) => {
if (state === 'background') {
// SDK persists state automatically, no action needed
}
});
return () => {
subscription.remove();
teardown.shutdown();
};
}, []);TypeScript
Strict Types
All SDK types are exported:
import type {
TeardownCore,
TeardownCoreOptions,
Session,
IdentifyState,
VersionStatus,
Persona,
AsyncResult,
} from '@teardown/force-updates';Type Guards
function handleIdentifyState(state: IdentifyState) {
switch (state.type) {
case 'unidentified':
// state is UnidentifiedSessionState
break;
case 'identifying':
// state is IdentifyingSessionState
break;
case 'identified':
// state is IdentifiedSessionState
console.log(state.session.user_id);
break;
}
}