react-native-device-activity

repository·main·Indexed 41 days ago

https://github.com/kingstinct/react-native-device-activity

A React Native wrapper for Apple's Screen Time, Device Activity, and Family Controls APIs. Version 1.0.1 enables developers to monitor app usage, implement content blocking and shielding, and manage web content filter policies. It provides access to the FamilyControl, ShieldConfiguration, ShieldAction, and ActivityMonitor APIs, including native SwiftUI selection components for app and website picking.

Tokens
13K
Snippets
29
Records
47
Agent score
51%

What's inside react-native-device-activity

  1. Understand the library data model

    main

    The library relies on persisting configuration and event history to UserDefaults. The key components are:

    • familyActivitySelectionId mapping: Maps a familyActivitySelection token to a reusable ID.
    • Triggers: Configures shield UI/actions and handles background tasks (web requests/notifications) from the Swift side. These are stored in UserDefaults with the prefix actions*for${goalId}.
    • Event history: Stores information about triggered events and timestamps, prefixed with events_${goalId} in UserDefaults.
    • ShieldIds: Used to reference shields in UserDefaults to reduce storage strain.
  2. Understand Apple's Screen Time APIs via react-native-device-activity

    main

    The library provides a React Native wrapper for several Apple Screen Time APIs. Depending on your use case, you may need one or all of the following:

    • FamilyControl API: Allows access to Screen Time data and managing restrictions on apps and websites (e.g., selecting apps to monitor or block).
    • ShieldConfiguration API: Customizes the visual appearance and text shown on the blocking screen UI when a user attempts to access blocked content.
    • ShieldAction API: Controls the behavior when users interact with buttons on the shield (e.g., what happens when they tap 'OK').
    • ActivityMonitor API: Schedules and manages when restrictions are applied or removed. This API is responsible for activating the shield even when your main app is killed.

    Note: These APIs require special approval and entitlements from Apple.

  3. Install in managed Expo projects

    main

    To use react-native-device-activity in a managed Expo project, follow these steps:

    1. Install the package:
    npm install react-native-device-activity
    # or
    yarn add react-native-device-activity
    1. Configure the Expo plugin in your app.json or app.config.js. You must provide your appleTeamId and an appGroup name:
    "plugins": [
        [
          "expo-build-properties",
          {
            "ios": {
              "deploymentTarget": "15.1"
            }
          }
        ],
        [
          "react-native-device-activity",
          {
            "appleTeamId": "<YOUR_TEAM_ID>",
            "appGroup": "group.<YOUR_APP_GROUP_NAME>"
          }
        ]
      ]
    1. Generate the native projects:
    npx expo prebuild --platform ios
    1. Verify Xcode Targets: Open the ios directory in Xcode. Ensure the following targets exist alongside your main app target:
    • ActivityMonitorExtension
    • ShieldAction
    • ShieldConfiguration
  4. Select apps to track using SelectionView components

    main

    To track, block, or whitelist specific apps, you must obtain a familyActivitySelection token from the user. This is done by presenting a native picker. The library provides several components for different UX patterns:

    • DeviceActivitySelectionSheetView: Uses Apple's native .familyActivityPicker flow with system Cancel/Done controls. It acts as an invisible anchor in your layout and fires onDismissRequest when the user finishes.
    • DeviceActivitySelectionView: Renders the picker inline. Use this if you want full control over the presentation (e.g., wrapping it in a React Native <Modal>) or need a custom fallback UI.
    • *Persisted variants: Use DeviceActivitySelectionSheetViewPersisted or DeviceActivitySelectionViewPersisted to work with a familyActivitySelectionId. This is recommended for large selection tokens to avoid passing massive base64 strings across the JS bridge.

    Note: The picker is a SwiftUI view and can be prone to crashing when browsing large categories. It is recommended to provide a fallback view behind it.

    // Example: Using the inline view in a Modal
    <Modal visible={visible} animationType="slide" presentationStyle="pageSheet">
      <View style={{ flex: 1 }}>
        <DeviceActivitySelectionView
          style={{ flex: 1, width: "100%" }}
          onSelectionChange={handleSelectionChange}
          familyActivitySelection={familyActivitySelection}
        />
      </View>
    </Modal>
  5. Request Apple Family Controls entitlement

    main

    Using these APIs requires the Family Controls (distribution) entitlement from Apple.

    Important Requirements:

    • Request approval from Apple as early as possible via their request form.
    • You must request approval for every bundle identifier you intend to use. If you use all native extensions, you need approval for 4 identifiers:
      • com.your-bundleIdentifier (Main App)
      • com.your-bundleIdentifier.ActivityMonitor
      • com.your-bundleIdentifier.ShieldAction
      • com.your-bundleIdentifier.ShieldConfiguration
    • Once approved, manually add the "Family Controls (Distribution)" capability under Additional Capabilities for each identifier on the Apple Developer portal.

    Note: Until approved, you are limited to local development builds in Xcode and cannot use Expo Dev Clients.

  6. Configure Apple target extensions for Expo

    main

    When using react-native-device-activity in a managed Expo project, you can use createConfig to generate the necessary configuration for Apple target extensions. This function ensures that the extensions are granted the required family-controls entitlement and are correctly associated with your app's App Group for data sharing.

    Supported targetType values:

    • shield-action (ShieldAction API)
    • shield-configuration (ShieldConfiguration API)
    • device-activity-monitor (ActivityMonitor API)

    Note: This function relies on @kingstinct/expo-apple-targets to handle the underlying Expo configuration logic.

    const { createConfig } = require('react-native-device-activity/config-plugin/createExpoTargetConfig');
    
    // Example usage in an Expo config plugin
    module.exports = (config) => {
      const shieldActionConfig = createConfig('shield-action')(config);
      const shieldConfigurationConfig = createConfig('shield-configuration')(config);
      const activityMonitorConfig = createConfig('device-activity-monitor')(config);
      
      // These configurations are then used with @kingstinct/expo-apple-targets
      return config;
    };
  7. Configure the Activity Monitor Extension via Expo Config Plugin

    main

    To use react-native-device-activity in a managed Expo project, use the withActivityMonitorExtensionPlugin config plugin. This plugin automates the setup of Apple targets, entitlements, App Groups, and Xcode settings required for the Activity Monitor extension to function.

    Required Configuration

    You must provide an appGroup property in your plugin options. This identifier is used to enable communication between your main app and the device activity extension.

    Plugin Options

    OptionTypeRequiredDescription
    appGroupstringYesThe App Group identifier (e.g., group.com.yourname.app) used for sharing data between the app and the extension.
    appleTeamIdstringNoYour Apple Developer Team ID.
    matchstringNoUsed for Apple Match certificates.
    copyToTargetFolderbooleanNoWhether to copy the target folder.
    // Example usage in app.config.js or app.json
    export default ({ config }) => ({
      ...config,
      plugins: [
        [
          "react-native-device-activity",
          {
            "appGroup": "group.com.yourdomain.yourapp"
          }
        ]
      ]
    });
  8. Configure Apple entitlements using withEntitlementsPlugin

    main

    When using react-native-device-activity in an Expo project, you must use the withEntitlementsPlugin config plugin to ensure the necessary Apple entitlements are added to your app. This plugin performs two critical tasks:

    1. Adds Family Controls entitlement: Sets com.apple.developer.family-controls to true.
    2. Configures App Groups: Adds the specified appGroup to the com.apple.security.application-groups array, ensuring it doesn't create duplicates.

    To use this, include the plugin in your app.json or app.config.js and provide the appGroup identifier required for your App Group setup.

    // Example usage in app.config.js
    module.exports = {
      expo: {
        plugins: [
          [
            'react-native-device-activity/withEntitlements',
            { appGroup: 'group.com.yourname.yourapp' }
          ]
        ]
      }
    };
  9. Configure App Group via Expo Config Plugin

    main

    To enable App Group support in a managed Expo project, use the withInfoPlistAppGroup config plugin. This plugin modifies the Info.plist to include the REACT_NATIVE_DEVICE_ACTIVITY_APP_GROUP key, which allows the native module to communicate across different app extensions (like Shield extensions) using a shared App Group identifier.

    Pass an object containing the appGroup string to the plugin in your app.config.js or app.json.

    // Example usage in app.config.js
    module.exports = ({ config }) => ({
      ...config,
      plugins: [
        [
          'react-native-device-activity/withInfoPlistAppGroup',
          { appGroup: 'group.com.your.identifier' }
        ],
      ],
    });
  10. Handle authorization state changes and delays

    main

    The Screen Time APIs have specific behaviors regarding authorization that developers should account for:

    • External Authorization Changes: If a user changes Screen Time permissions outside of your app, the native API will not reflect this change until the app is restarted. If you detect a state change is needed, you may need to prompt the user to restart the app.
    • Permission Requesting: Unlike most iOS authorization flows, you can request and revoke permissions multiple times, even if the user has previously denied them.
    • notDetermined Status Delay: When calling getAuthorizationStatus, the API may return notDetermined even after a user has made a choice due to internal delays. To handle this, use the pollAuthorizationStatus convenience function to poll the status until the correct state is returned.