Skip to content

nlbb/wellspent-react-native-sdk-docs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Wellspent React Native SDK

This guide provides step-by-step instructions to integrate the Wellspent React Native SDK into your React Native application. The SDK enables features like device activity monitoring, app shielding, and habit tracking using iOS Family Controls and custom configurations.

Table of Contents

Prerequisites

Before you begin, ensure you have the following installed:

  • macOS: 15.0 or higher with Xcode 16.0 or higher
  • Node: Version 20 or higher
  • Yarn: Version 3 or higher (or npm 8 or higher)
  • React Native CLI: Installed globally
    npm install -g react-native-cli
  • CocoaPods: Version 1.11 or higher
    sudo gem install cocoapods
  • A React Native app (new or existing)

Creating a New React Native Project (Optional)

If you have an existing React Native project, skip to Installing the RNWellspentSDK Wrapper. Otherwise, create a new project:

npm install -g react-native-cli
react-native init WellspentApp
cd WellspentApp

This creates a project structure like:

WellspentApp/
├── android/
├── ios/
├── App.js
├── index.js
├── package.json
└── …

Installing the RNWellspentSDK Wrapper

Install the Wellspent React Native SDK wrapper from GitHub using one of the following commands:

  • Using Yarn:

    yarn add git+https://github.com/nlbb/wellspent-reactnative-sdk.git#main
  • Using npm:

    npm install --save git+https://github.com/nlbb/wellspent-reactnative-sdk.git#main

Verify the installation by checking the node_modules directory:

ls node_modules/react-native-wellspentsdk/ios

You should see at least:

RNWellspentSDK.podspec
RNWellspentSDK.swift

Configuring iOS Extension Targets

Creating Extension Targets

You need to create three iOS extension targets in Xcode before editing the Podfile:

  1. Open the project in Xcode (not the workspace, as it doesn’t exist yet):

    open ios/WellspentApp.xcodeproj
  2. In Xcode’s Project Navigator, select the WellspentApp project.

  3. Go to File → New → Target….

  4. Create the following extensions using the specified templates and product names:

    • Device Activity Monitor Extension: Name it DeviceActivityMonitorExtension
    • Shield Action Extension: Name it ShieldActionExtension
    • Shield Configuration Extension: Name it ShieldConfigurationExtension

After adding, your Xcode TARGETS should include:

  • WellspentApp
  • DeviceActivityMonitorExtension
  • ShieldActionExtension
  • ShieldConfigurationExtension

Enabling App Group and Family Controls

All four targets (WellspentApp, DeviceActivityMonitorExtension, ShieldActionExtension, ShieldConfigurationExtension) must share an App Group and include the Family Controls entitlement.

Adding App Group

  1. In Xcode, select DeviceActivityMonitorExtensionSigning & Capabilities.
  2. Click + Capability → Choose App Groups.
  3. Add the App Group:
    group.com.mycompany.wellspent
    
  4. Repeat for ShieldActionExtension, ShieldConfigurationExtension, and WellspentApp.

Verify that each target’s Signing & Capabilities lists:

  • App Groups: group.com.mycompany.wellspent

Update each extension’s Info.plist to include:

<key>AppGroup</key>
<string>group.com.mycompany.wellspent</string>

Adding Family Controls Entitlement

  1. Select WellspentAppSigning & Capabilities.
  2. Click + Capability → Add Family Controls.
  3. Repeat for DeviceActivityMonitorExtension, ShieldActionExtension, and ShieldConfigurationExtension.

Each target’s Signing & Capabilities should now list:

  • App Groups: group.com.mycompany.wellspent
  • Family Controls

Note: For App Store distribution, request the “Family Controls (Distribution)” entitlement from Apple. For local development, adding the capability is sufficient.

Replacing Extension Boilerplate Code

Replace the boilerplate code in each extension’s Swift file to subclass the Wellspent extension classes:

  • DeviceActivityMonitorExtension.swift:

    import Extension_DeviceActivityMonitor
    
    class DeviceActivityMonitorExtension: Extension_DeviceActivityMonitor.ActivityMonitorExtension {}
  • ShieldActionExtension.swift:

    import Extension_ShieldAction
    
    class ShieldActionExtension: Extension_ShieldAction.ShieldActionExtension {}
  • ShieldConfigurationExtension.swift:

    import Extension_ShieldConfiguration
    
    class ShieldConfigurationExtension: Extension_ShieldConfiguration.ShieldConfigurationExtension {}

No additional code is needed in these files.

Setting Up the Podfile

Creating a Minimal Podfile

In the ios/ folder, create or edit Podfile with the following content:

require_relative '../node_modules/react-native/scripts/react_native_pods'

platform :ios, '16.0'
prepare_react_native_project!

target 'WellspentApp' do
  use_frameworks!

  # Install RNWellspentSDK (React Native wrapper)
  pod 'RNWellspentSDK',
      :path => '../node_modules/react-native-wellspentsdk/ios',
      :modular_headers => true

  # Install React Native’s own pods
  use_react_native!(
    :path => '../node_modules/react-native',
    :hermes_enabled => false,
    :fabric_enabled => false
  )
end

# Extension targets will be added later

Troubleshooting:

  • If you encounter:

    [!] CocoaPods could not find compatible versions for pod "RNWellspentSDK":
    

    Ensure the platform :ios, '16.0' is set in the Podfile.

  • If you see:

    ArgumentError - [Xcodeproj] Unable to find compatibility version string for object version `70`.
    

    Edit ios/WellspentApp.xcodeproj/project.pbxproj and change:

    - objectVersion = 70;
    + objectVersion = 60;

Running pod install

Run the following commands:

cd ios
rm -rf Pods Podfile.lock
pod install

This generates WellspentApp.xcworkspace in the ios/ folder.

Opening the Xcode Workspace

Always use the workspace from now on:

open ios/WellspentApp.xcworkspace

The Project Navigator should show:

WellspentApp.xcworkspace
├── Pods/
├── WellspentApp/
├── DeviceActivityMonitorExtension/
├── ShieldActionExtension/
├── ShieldConfigurationExtension/
└── …

Updating Podfile for Extensions

Update the Podfile incrementally to include extension targets and header search paths. After each change, run:

cd ios
pod install

Adding Strip Duplicate XCFramework Signatures Script

To avoid duplicate signature errors during archiving, add a Run Script in Xcode:

  1. Open WellspentApp.xcworkspace.
  2. Select the WellspentApp target → Build Phases.
  3. Click +New Run Script Phase.
  4. Drag the script below [CP] Embed Pods Frameworks and above [CP] Copy Pods Resources.
  5. Rename the phase to Strip Duplicate XCFramework Signatures.
  6. Paste the following script:
    # Remove _every_ xcframework .signature file before Archive:
    if [ "${XCODE_VERSION_MAJOR}" -ge 1500 ]; then
      echo "🧹 Removing all duplicate xcframework signature files…"
      find "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORKS_FOLDER_PATH}" \
           -type f -name "*.xcframework-*.signature" \
           -print -delete
    fi
  7. Ensure “For install builds only” is unchecked.

Adding Wellspent iOS SDK via Swift Package Manager

  1. In Xcode, go to File → Add Packages….
  2. Enter the Wellspent iOS SDK URL (requires access):
    https://github.com/nlbb/Wellspent-iOS-SDK-Binaries.git
    
  3. In the package popup, assign products to targets:
Package Product Kind Add to Target
Extension_DeviceActivityMonitor Library DeviceActivityMonitor
Extension_ShieldAction Library ShieldAction
Extension_ShieldConfiguration Library ShieldConfiguration
Wellspent Library WellspentApp
WellspentSDK Library None
  1. For the Pods project, assign:

    Package Product Kind Add to Target
    Wellspent Library RNWellspentSDK
  2. Click Add Package.

This resolves any “No such module” errors in extension classes.

Adding Family Controls Usage Description

Add the following to ios/WellspentApp/Info.plist:

<key>NSFamilyControlsUsageDescription</key>
<string>Wellspent needs permission to monitor your device usage so it can lock distracting apps.</string>

Building, Running, and Archiving

  1. (Optional) Clean DerivedData:

    rm -rf ~/Library/Developer/Xcode/DerivedData
  2. In Xcode, select the WellspentApp scheme and a Generic iOS Device or physical iPhone.

  3. Build the project (⌘+B). Ensure no errors like “No such module ‘WellspentSDK’.”

  4. Run on a device or simulator (⌘+R). On a real iPhone (iOS 16+), you’ll see the Family Controls UI.

  5. Archive the project (Product → Archive). The log should show:

    🧹 Removing all duplicate xcframework signature files…
    

The archive should succeed without signature errors.

JavaScript Usage Example

Replace your App.js or App.tsx with the following TypeScript example to interact with the Wellspent SDK:

// File: example/App.tsx

import React, { useEffect, useState } from 'react';
import {
  View,
  Text,
  Button,
  StyleSheet,
  NativeModules,
  NativeEventEmitter,
  Alert,
  ActivityIndicator,
} from 'react-native';

/** 1) Type definitions for our native module */
export interface WellspentThemeValues {
  backgroundPrimary: string;         // e.g. "#FFFFFF"
  backgroundSecondary: string;
  backgroundTertiary: string;
  backgroundShield: string;
  labelPrimary: string;
  labelSecondary: string;
  labelTertiary: string;
  backgroundButton: string;
  backgroundButtonDisabled: string;
  buttonLabel: string;
  buttonLabelDisabled: string;
  fills: string;
}

interface RNWellspentSDKType {
  configure(
    apiKey: string,
    name: string,
    bundleId: string,
    appGroupIdentifier: string,
    activity?: string | null,
    successCriteria?: string | null,
    theme?: WellspentThemeValues
  ): Promise<boolean>;

  startOnboarding(): Promise<boolean>;
  showNudgeSettings(): Promise<boolean>;
  completeDailyHabit(): void;
  isHabitCompleted(): Promise<boolean>;
  emitEvent(name: string, body: any): void;
}

/** 2) Grab the native module from NativeModules */
const { RNWellspentSDK } = NativeModules as {
  RNWellspentSDK: RNWellspentSDKType;
};

/** 3) Set up an event emitter for native events */
const eventEmitter = new NativeEventEmitter(RNWellspentSDK);

const App: React.FC = () => {
  /** 4) Local state to track configuration status */
  const [isConfiguring, setIsConfiguring] = useState(true);
  const [configured, setConfigured] = useState(false);

  useEffect(() => {
    /** 5) Subscribe to native events */
    // a) Generic SDK events:
    const sdkSub = eventEmitter.addListener(
      'WellspentEvent',
      (event) => {
        console.log('[WellspentSDKEvent]', event.name, event.body);
        Alert.alert(`SDK Event: ${event.name}`, JSON.stringify(event.body));
      }
    );

    // b) Screen‐view tracking events:
    const screenSub = eventEmitter.addListener(
      'WellspentTrackScreen',
      (event) => {
        console.log('[Track Screen]', event.screenName, event.properties);
        // Forward this to your analytics provider if needed
        Alert.alert(
          'Tracking ‒ Screen View',
          `Screen: ${event.screenName}\nProps: ${JSON.stringify(event.properties)}`
        );
      }
    );

    // c) Generic event tracking events:
    const eventSub = eventEmitter.addListener(
      'WellspentTrackEvent',
      (event) => {
        console.log('[Track Event]', event.eventName, event.properties);
        // Forward this to your analytics provider if needed
        Alert.alert(
          'Tracking ‒ Event',
          `Event: ${event.eventName}\nProps: ${JSON.stringify(event.properties)}`
        );
      }
    );

    return () => {
      sdkSub.remove();
      screenSub.remove();
      eventSub.remove();
    };
  }, []);

  useEffect(() => {
    /** 6) On mount, call configure(...) exactly once */
    const runConfigure = async () => {
      try {
        setIsConfiguring(true);

        const ok = await RNWellspentSDK.configure(
          // ← your actual API key & params go here
          'ios-ws-sdk_sandbox_aa7d600559446aa936b063d81e8a9de48a33c65b3e2705f066d7c87d65b6a7dc',
          'Kilimanjaro',
          'co.mindamins.WellspentB2BRN',
          'group.co.wellspent',
          'learning',
          'math lesson',
          {
            backgroundPrimary: '#FFFFFF',
            backgroundSecondary: '#F2F2F2',
            backgroundTertiary: '#E5E5E5',
            backgroundShield: '#000000',
            labelPrimary: '#111111',
            labelSecondary: '#333333',
            labelTertiary: '#666666',
            backgroundButton: '#007AFF',
            backgroundButtonDisabled: '#CCCCCC',
            buttonLabel: '#FFFFFF',
            buttonLabelDisabled: '#888888',
            fills: '#00C853',
          }
        );

        if (!ok) {
          Alert.alert(
            'Configuration Failed',
            'Wellspent SDK could not initialize. Please verify API key, bundle ID, and app group.'
          );
          setConfigured(false);
        } else {
          setConfigured(true);
        }
      } catch (error) {
        console.error('WellspentSDK.configure error', error);
        Alert.alert('Error', 'Failed to initialize Wellspent SDK.');
        setConfigured(false);
      } finally {
        setIsConfiguring(false);
      }
    };

    runConfigure();
    // Note: no dependencies → runs once per app launch
  }, []);

  /** 7) Handler for “Start Onboarding” (only calls if configured) */
  const handleStartWellspent = async () => {
    if (!configured) {
      Alert.alert('Not Ready', 'Still configuring, please wait a moment.');
      return;
    }

    try {
      const completed = await RNWellspentSDK.startOnboarding();
      if (completed) {
        Alert.alert('Onboarding Complete', 'User has finished the onboarding flow.');
      } else {
        Alert.alert('Onboarding Canceled', 'User dismissed the onboarding flow.');
      }
    } catch (error) {
      console.error('WellspentSDK.startOnboarding error', error);
      Alert.alert('Error', 'Failed to launch onboarding.');
    }
  };

  /** 8) Other button handlers remain unchanged */
  const handleShowNudgeSettings = async () => {
    if (!configured) {
      Alert.alert('Not Ready', 'Still configuring, please wait a moment.');
      return;
    }
    try {
      await RNWellspentSDK.showNudgeSettings();
      Alert.alert('Nudge Settings', 'User has closed the nudge settings.');
    } catch (error) {
      console.error('WellspentSDK.showNudgeSettings error', error);
      Alert.alert('Error', 'Failed to show nudge settings.');
    }
  };

  const handleCompleteHabit = () => {
    if (!configured) {
      Alert.alert('Not Ready', 'Still configuring, please wait a moment.');
      return;
    }
    RNWellspentSDK.completeDailyHabit();
    Alert.alert('Habit Completed', 'Marked today’s habit as completed.');
  };

  const handleCheckHabit = async () => {
    if (!configured) {
      Alert.alert('Not Ready', 'Still configuring, please wait a moment.');
      return;
    }
    try {
      const done = await RNWellspentSDK.isHabitCompleted();
      Alert.alert('Habit Status', done ? 'Already completed today.' : 'Not completed yet.');
    } catch (error) {
      console.error('WellspentSDK.isHabitCompleted error', error);
      Alert.alert('Error', 'Failed to check habit status.');
    }
  };

  /** 9) While configuring is in progress, show an ActivityIndicator */
  if (isConfiguring) {
    return (
      <View style={styles.loadingContainer}>
        <ActivityIndicator size="large" color="#007AFF" />
        <Text style={styles.loadingText}>Configuring Wellspent SDK…</Text>
      </View>
    );
  }

  /** 10) Once configuration has finished, show the normal UI */
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Wellspent React Native Demo</Text>

      <View style={styles.buttonContainer}>
        <Button
          title="Start Onboarding"
          onPress={handleStartWellspent}
          disabled={!configured}
        />
      </View>

      <View style={styles.buttonContainer}>
        <Button
          title="Show Nudge Settings"
          onPress={handleShowNudgeSettings}
          disabled={!configured}
        />
      </View>

      <View style={styles.buttonContainer}>
        <Button
          title="Complete Daily Habit"
          onPress={handleCompleteHabit}
          disabled={!configured}
        />
      </View>

      <View style={styles.buttonContainer}>
        <Button
          title="Check Habit Status"
          onPress={handleCheckHabit}
          disabled={!configured}
        />
      </View>
    </View>
  );
};

export default App;

/** 11) Styles */
const styles = StyleSheet.create({
  loadingContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingHorizontal: 24,
    backgroundColor: '#F5F5F5',
  },
  loadingText: {
    marginTop: 12,
    fontSize: 16,
    color: '#333',
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingHorizontal: 24,
    backgroundColor: '#F5F5F5',
  },
  title: {
    fontSize: 22,
    fontWeight: '600',
    textAlign: 'center',
    marginBottom: 24,
  },
  buttonContainer: {
    marginVertical: 8,
  },
});

This example demonstrates how to configure the SDK, start onboarding, manage nudge settings, track habits, and emit custom events.

About

Documentation on how to integrate Wellspent SDK into react native

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published