# react-native-device-activity `react-native-device-activity` is a React Native library (v0.6.1) that provides a TypeScript/JavaScript wrapper around Apple's Screen Time, Device Activity, and Family Controls APIs on iOS 15+. It enables developers to monitor app usage, block applications on schedules, customize the blocking shield UI, set web content filter policies, and react to device activity events — all from React Native code. The library is built with Expo Modules Core and requires special entitlements from Apple (Family Controls distribution) to be used in production builds. The library is organized around four core Apple APIs: **FamilyControls** (authorization and app selection), **DeviceActivity** (scheduling monitors and threshold events), **ManagedSettings** (applying blocks, whitelists, and web filters), and **ManagedSettingsUI** (configuring the shield appearance shown to users when content is blocked). All persistent state is stored in a shared `UserDefaults` App Group so native extension processes (ActivityMonitor, ShieldAction, ShieldConfiguration) can read and act on configurations even when the main app is in the background or killed. --- ## Installation Install the package and configure the Expo plugin with your Apple Team ID and App Group. ```bash npm install react-native-device-activity ``` ```json // app.json { "plugins": [ ["expo-build-properties", { "ios": { "deploymentTarget": "15.1" } }], [ "react-native-device-activity", { "appleTeamId": "ABCDE12345", "appGroup": "group.com.myapp.screentime" } ] ] } ``` ```bash npx expo prebuild --platform ios ``` --- ## `isAvailable` Returns `true` only on iOS 15+ with the native module loaded; safe to call on Android without crashing. ```typescript import { isAvailable } from 'react-native-device-activity'; if (!isAvailable()) { console.log('Screen Time APIs not available on this platform/version'); return; } ``` --- ## `requestAuthorization` / `revokeAuthorization` / `getAuthorizationStatus` `requestAuthorization` prompts the user for Screen Time permission (individual or child account). `getAuthorizationStatus` synchronously returns the current status (0 = notDetermined, 1 = denied, 2 = approved). `revokeAuthorization` revokes the permission and returns the updated status. ```typescript import { requestAuthorization, revokeAuthorization, getAuthorizationStatus, AuthorizationStatus, } from 'react-native-device-activity'; // Request permission on mount useEffect(() => { async function setup() { const status = getAuthorizationStatus(); if (status === AuthorizationStatus.notDetermined) { try { await requestAuthorization('individual'); // or 'child' } catch (e) { console.error('Authorization failed:', e); } } } setup(); }, []); // Revoke later const handleRevoke = async () => { const newStatus = await revokeAuthorization(); console.log('Status after revoke:', newStatus); // 0 | 1 | 2 }; ``` --- ## `useAuthorizationStatus` A React hook that reactively tracks authorization status changes (including changes made outside the app on next foreground). ```typescript import { useAuthorizationStatus, AuthorizationStatus } from 'react-native-device-activity'; function AuthBanner() { const authorizationStatus = useAuthorizationStatus(); const label = { [AuthorizationStatus.notDetermined]: 'Not determined', [AuthorizationStatus.denied]: 'Denied', [AuthorizationStatus.approved]: 'Approved ✅', }[authorizationStatus]; return {label}; } ``` --- ## `pollAuthorizationStatus` Polls `getAuthorizationStatus` at an interval until a non-`notDetermined` status is returned or max attempts is reached. Useful immediately after `requestAuthorization` since the native API can lag. ```typescript import { requestAuthorization, pollAuthorizationStatus, AuthorizationStatus } from 'react-native-device-activity'; const handleAuth = async () => { await requestAuthorization('individual'); const finalStatus = await pollAuthorizationStatus({ pollIntervalMs: 300, maxAttempts: 15, }); if (finalStatus === AuthorizationStatus.approved) { console.log('Ready to monitor'); } else { console.warn('User denied or timed out:', finalStatus); } }; ``` --- ## `onAuthorizationStatusChange` Subscribes to native authorization status change events. Returns an `EventSubscription` — call `.remove()` to unsubscribe. ```typescript import { onAuthorizationStatusChange, AuthorizationStatus } from 'react-native-device-activity'; useEffect(() => { const sub = onAuthorizationStatusChange(({ authorizationStatus }) => { if (authorizationStatus === AuthorizationStatus.approved) { console.log('User approved Screen Time access'); } }); return () => sub.remove(); }, []); ``` --- ## `DeviceActivitySelectionView` An inline native iOS SwiftUI view that lets the user pick apps, app categories, and web domains to monitor or block. Prone to crashes on large category browsing — always provide a fallback UI behind it. ```typescript import { useState } from 'react'; import { Modal, View } from 'react-native'; import { DeviceActivitySelectionView } from 'react-native-device-activity'; function AppPicker({ visible, onDismiss }) { const [selection, setSelection] = useState(null); return ( {/* Fallback behind native view in case of SwiftUI crash */} Loading picker… { // event.nativeEvent: { familyActivitySelection, applicationCount, categoryCount, webDomainCount, includeEntireCategory } console.log(`Selected ${event.nativeEvent.applicationCount} apps`); setSelection(event.nativeEvent.familyActivitySelection); onDismiss(); }} /> ); } ``` --- ## `DeviceActivitySelectionSheetView` Uses Apple's native `.familyActivityPicker(isPresented:)` sheet with built-in Cancel/Done navigation controls. Mount it as a 1×1 invisible anchor; the native side presents the full-screen sheet automatically. ```typescript import { useState } from 'react'; import { DeviceActivitySelectionSheetView } from 'react-native-device-activity'; function NativeSheetPicker() { const [visible, setVisible] = useState(false); const [selection, setSelection] = useState(null); return ( <>