@poilabs/analysis-sdk-plugin
v1.5.2
Published
Official Expo Config Plugin for integrating the Poilabs Analysis SDK
Downloads
274
Readme
@poilabs/analysis-sdk-plugin
Official Expo Config Plugin for integrating the Poilabs Analysis SDK into Expo (prebuild) projects.
🚀 Automatically links native dependencies and modifies required iOS/Android files.
✨ What this plugin does
When used with expo prebuild, this plugin:
- ✅ Android Permissions: Adds all required permissions to
AndroidManifest.xml- Including
REQUEST_IGNORE_BATTERY_OPTIMIZATIONSfor doze mode prevention - Adds
android:foregroundServiceType="location"to theFOREGROUND_SERVICEpermission
- Including
- ✅ Android Native Integration: Automatically configures native modules
- Copies and registers
PoilabsPackageinMainApplication.kt - Adds Poilabs SDK dependency to
android/app/build.gradle - Configures JitPack repository in
android/build.gradle
- Copies and registers
- ✅ iOS Integration:
- Adds
pod 'PoilabsAnalysis'to the iOS Podfile - Adds
Info.plistkeys for Location and Bluetooth usage
- Adds
- ✅ Foreground Service Setup: Automatically configures foreground service intent for doze mode prevention
📦 Installation
Install the plugin to your Expo project:
npm install @poilabs/analysis-sdk-plugin
# or
yarn add @poilabs/analysis-sdk-pluginAlso install the required dependencies:
npx expo install expo-location expo-device⚙️ Configuration
Add the plugin to your app.json or app.config.js:
{
"expo": {
"plugins": [
[
"@poilabs/analysis-sdk-plugin",
{
"jitpackToken": "YOUR_JITPACK_TOKEN" // Get this from Poilabs
}
]
]
}
}Then run the prebuild command:
npx expo prebuildAndroid Setup (Automatic! 🎉)
The plugin now automatically handles Android setup:
✅ Auto-registers PoilabsPackage in MainApplication.kt
✅ Auto-adds REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission
✅ Auto-configures foreground service intent
Simply run:
npx expo prebuild
npx expo run:androidThat's it! No manual code changes needed.
⚠️ Android local.properties Warning
- You should create local.properties to android root
- and you should add this => sdk.dir=/Users/USERNAME/Library/Android/sdk
iOS Setup
For iOS, you need to ensure the plugin files are properly included in your Xcode project:
- Open your Xcode project
- In Xcode, verify that the PoilabsModule files are added to your project
- Check that the files appear in the "Build Phases > Compile Sources" section
- Find + button and click. Then you should "add other".
- If files are missing, you may need to manually add them from the iOS//PoilabsModule directory:
- PoilabsAnalysisModule.h
- PoilabsAnalysisModule.m
Pods Setup
- cd ios
- pod deintegrate
- pod install --repo-update
⚠️ iOS ARM64 Warning
Note: When developing for iOS, there's an important consideration regarding ARM64 architecture:
- If you're integrating into an existing project (which already has ARM64 support), you shouldn't encounter any issues.
- However, if you're creating a project from scratch, you need to remove the ARM64 reference from the Build Settings in Xcode. Otherwise, you might face compilation errors.
This setting is particularly important when developing on M series (Apple Silicon) Mac computers.
Then build and run your iOS project:
npx expo run:ios🚀 Usage
After the prebuild process, you can use the SDK in your application:
import { Image } from "expo-image";
import { useEffect, useState } from "react";
import { StyleSheet, TouchableOpacity } from "react-native";
import { HelloWave } from "@/components/HelloWave";
import ParallaxScrollView from "@/components/ParallaxScrollView";
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import {
startPoilabsAnalysis,
stopPoilabsAnalysis,
} from "@poilabs/analysis-sdk-plugin";
export default function HomeScreen() {
const [sdkStatus, setSdkStatus] = useState("Initializing...");
useEffect(() => {
const initAnalysis = async () => {
try {
// Start Poilabs SDK
const success = await startPoilabsAnalysis({
applicationId: "YOUR_APPLICATION_ID", // Get from Poilabs
applicationSecret: "YOUR_APPLICATION_SECRET", // Get from Poilabs
uniqueId: "USER_UNIQUE_ID", // A unique identifier for the user
});
setSdkStatus(success ? "Running ✅" : "Failed to start ❌");
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
setSdkStatus("Error: " + errorMessage);
}
};
initAnalysis();
}, []);
const handleStopSDK = () => {
try {
const result = stopPoilabsAnalysis();
setSdkStatus(result ? "Stopped ⛔" : "Failed to stop ❓");
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
setSdkStatus("Stop Error: " + errorMessage);
}
};
return (
<ParallaxScrollView
headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
headerImage={
<Image
source={require("@/assets/images/partial-react-logo.png")}
style={styles.reactLogo}
/>
}
>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
</ThemedView>
{/* SDK Status Indicator */}
<ThemedView style={styles.sdkStatusContainer}>
<ThemedText type="subtitle">Poilabs SDK: {sdkStatus}</ThemedText>
{/* Stop SDK Button */}
<TouchableOpacity style={styles.stopButton} onPress={handleStopSDK}>
<ThemedText style={styles.buttonText}>STOP SDK</ThemedText>
</TouchableOpacity>
</ThemedView>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
titleContainer: {
flexDirection: "row",
alignItems: "center",
gap: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: "absolute",
},
sdkStatusContainer: {
marginVertical: 16,
padding: 10,
borderRadius: 8,
backgroundColor: "#f5f5f5",
alignItems: "center",
},
stopButton: {
marginTop: 10,
backgroundColor: "#FF3B30",
paddingVertical: 8,
paddingHorizontal: 16,
borderRadius: 6,
},
buttonText: {
color: "white",
fontWeight: "bold",
},
});🔋 Doze Mode Prevention (Android)
The plugin automatically configures the foreground service intent when you start the SDK. This enables doze mode prevention right out of the box!
For apps that need continuous beacon scanning, you can optionally customize the foreground service notification:
import { configureAnalysisSDK } from "@poilabs/analysis-sdk-plugin";
// Optional: Customize foreground service notification
await configureAnalysisSDK({
enabled: true,
enableForegroundService: true,
serviceNotificationTitle: 'Poilabs SDK Active',
notificationChannelName: 'Poilabs SDK Service',
notificationChannelDescription: 'Keeps Poilabs SDK running during doze mode'
});What happens automatically:
- ✅ Foreground service intent is auto-configured on SDK startup
- ✅
REQUEST_IGNORE_BATTERY_OPTIMIZATIONSpermission is auto-added - ✅ Android's doze mode won't stop beacon scanning
- ✅ Continuous analytics even when device is idle
Why this is important:
- Android's doze mode stops background processes including beacon scanning
- The SDK has built-in foreground service support to prevent this
- Essential for production apps that need reliable beacon detection
📡 iOS Monitoring Events (New in v1.5.0)
Monitor real-time beacon detection, server responses, and SDK status updates on iOS. These features allow you to track SDK activity and handle events in your React Native application.
Basic Monitoring Setup
import {
startPoilabsAnalysis,
addMonitoringListener,
removeAllListeners
} from '@poilabs/analysis-sdk-plugin';
import { useEffect, useState } from 'react';
function BeaconMonitoring() {
const [events, setEvents] = useState([]);
useEffect(() => {
// Initialize SDK
const initSDK = async () => {
await startPoilabsAnalysis({
applicationId: 'YOUR_APP_ID',
applicationSecret: 'YOUR_SECRET',
uniqueId: 'user-123'
});
};
// Setup event listeners
const unsubscribeBeacon = addMonitoringListener('onBeaconDetected', (event) => {
console.log('Beacon detected:', event);
setEvents(prev => [...prev, { type: 'beacon', data: event }]);
});
const unsubscribeResponse = addMonitoringListener('onBeaconResponse', (event) => {
console.log('Server response:', event);
setEvents(prev => [...prev, { type: 'response', data: event }]);
});
const unsubscribeError = addMonitoringListener('onError', (event) => {
console.error('SDK error:', event);
setEvents(prev => [...prev, { type: 'error', data: event }]);
});
const unsubscribeStatus = addMonitoringListener('onStatusUpdate', (event) => {
console.log('Status update:', event);
setEvents(prev => [...prev, { type: 'status', data: event }]);
});
initSDK();
// Cleanup
return () => {
unsubscribeBeacon();
unsubscribeResponse();
unsubscribeError();
unsubscribeStatus();
// Or use: removeAllListeners();
};
}, []);
return (
<View>
<Text>Monitoring Events ({events.length})</Text>
{events.map((event, index) => (
<Text key={index}>{event.type}: {JSON.stringify(event.data)}</Text>
))}
</View>
);
}Event Types
onBeaconDetected
Emitted when a beacon is detected by the SDK.
Event Data:
{
beaconId?: string;
rssi?: number;
timestamp: number;
uuid?: string;
major?: number;
minor?: number;
source: string; // 'delegate_method' | 'notification' | 'test_method'
[key: string]: any;
}onBeaconResponse
Emitted when the SDK receives a response from the server about beacon monitoring.
Event Data:
{
success: boolean;
data?: any;
error?: string;
timestamp: number;
source: string;
[key: string]: any;
}onError
Emitted when the SDK encounters an error.
Event Data:
{
code: number;
message: string;
domain: string;
timestamp: number;
source: string;
}onStatusUpdate
Emitted when the SDK status changes.
Event Data:
{
status: 'started' | 'stopped' | 'scanning' | 'idle' | 'error' | 'unknown';
message?: string;
timestamp: number;
source: string;
}Monitoring Methods
addMonitoringListener(eventType, callback)
Adds an event listener for monitoring events.
Parameters:
eventType(string): One of'onBeaconDetected','onBeaconResponse','onError','onStatusUpdate'callback(function): Function to call when event is emitted
Returns:
Function: Unsubscribe function to remove the listener
Example:
const unsubscribe = addMonitoringListener('onBeaconDetected', (event) => {
console.log('Beacon:', event.beaconId, 'RSSI:', event.rssi);
});
// Later, to remove the listener:
unsubscribe();removeAllMonitoringListeners(eventType?)
Removes all listeners for a specific event type, or all listeners if no type is specified.
Parameters:
eventType(string, optional): Event type to remove listeners for
Example:
// Remove all listeners for beacon detection
removeAllMonitoringListeners('onBeaconDetected');
// Remove all listeners for all event types
removeAllMonitoringListeners();removeAllListeners()
Removes all monitoring event listeners.
Example:
removeAllListeners();Testing Event System
For debugging purposes, you can test if the event system is working correctly:
import { testEventEmission } from '@poilabs/analysis-sdk-plugin';
// Test event emission (iOS only)
const testResult = await testEventEmission();
if (testResult) {
console.log('✅ Event system working - test events should be received');
} else {
console.log('❌ Event system not working - check listeners are setup');
}Important Notes
- iOS Only: Monitoring events are currently only available on iOS. Android returns
falsewith warnings. - Physical Device: Beacon monitoring requires a physical iOS device - it won't work in the simulator.
- Permissions: Ensure Location and Bluetooth permissions are granted before starting monitoring.
- Background Mode: For background monitoring, ensure proper iOS background modes are configured in your app.
- Setup Listeners First: Always setup event listeners before calling
startPoilabsAnalysis()to ensure you don't miss any events. - Memory Management: Always cleanup listeners when component unmounts to prevent memory leaks.
Complete Example
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import {
startPoilabsAnalysis,
stopPoilabsAnalysis,
addMonitoringListener,
removeAllListeners
} from '@poilabs/analysis-sdk-plugin';
export default function MonitoringScreen() {
const [events, setEvents] = useState([]);
const [sdkStatus, setSdkStatus] = useState('initializing');
useEffect(() => {
let isActive = true;
const initializeMonitoring = async () => {
try {
// Setup all event listeners FIRST
addMonitoringListener('onBeaconDetected', (event) => {
if (!isActive) return;
console.log('🔵 Beacon:', event);
setEvents(prev => [{ id: Date.now(), type: 'Beacon', data: event }, ...prev.slice(0, 49)]);
});
addMonitoringListener('onBeaconResponse', (event) => {
if (!isActive) return;
console.log('🌐 Response:', event);
setEvents(prev => [{ id: Date.now(), type: 'Response', data: event }, ...prev.slice(0, 49)]);
});
addMonitoringListener('onError', (event) => {
if (!isActive) return;
console.error('❌ Error:', event);
setEvents(prev => [{ id: Date.now(), type: 'Error', data: event }, ...prev.slice(0, 49)]);
});
addMonitoringListener('onStatusUpdate', (event) => {
if (!isActive) return;
console.log('📊 Status:', event);
setSdkStatus(event.status);
});
// Then initialize SDK
const result = await startPoilabsAnalysis({
applicationId: 'YOUR_APP_ID',
applicationSecret: 'YOUR_SECRET',
uniqueId: 'user-' + Date.now()
});
if (result) {
console.log('✅ SDK initialized and monitoring active');
setSdkStatus('running');
} else {
console.error('❌ SDK initialization failed');
setSdkStatus('failed');
}
} catch (error) {
console.error('Initialization error:', error);
setSdkStatus('error');
}
};
initializeMonitoring();
// Cleanup
return () => {
isActive = false;
removeAllListeners();
stopPoilabsAnalysis();
};
}, []);
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Poilabs Monitoring</Text>
<Text style={styles.status}>Status: {sdkStatus}</Text>
<Text style={styles.count}>Events: {events.length}</Text>
</View>
<FlatList
data={events}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.eventItem}>
<Text style={styles.eventType}>{item.type}</Text>
<Text style={styles.eventTime}>
{new Date(item.data.timestamp).toLocaleTimeString()}
</Text>
<Text style={styles.eventData} numberOfLines={3}>
{JSON.stringify(item.data, null, 2)}
</Text>
</View>
)}
ListEmptyComponent={
<Text style={styles.emptyText}>
No events yet. Get near beacons to see activity.
</Text>
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#fff' },
header: { padding: 20, backgroundColor: '#f5f5f5', borderBottomWidth: 1, borderBottomColor: '#ddd' },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 5 },
status: { fontSize: 14, color: '#666', marginBottom: 3 },
count: { fontSize: 14, color: '#666' },
eventItem: { padding: 15, borderBottomWidth: 1, borderBottomColor: '#eee' },
eventType: { fontSize: 16, fontWeight: 'bold', color: '#007AFF', marginBottom: 5 },
eventTime: { fontSize: 12, color: '#999', marginBottom: 5 },
eventData: { fontSize: 11, fontFamily: 'monospace', color: '#333' },
emptyText: { padding: 50, textAlign: 'center', color: '#999', fontSize: 14 }
});📝 API Reference
startPoilabsAnalysis(config)
Starts the Poilabs Analysis SDK with the given configuration.
Parameters
config(Object):applicationId(String): The application ID provided by PoilabsapplicationSecret(String): The application secret provided by PoilabsuniqueId(String): A unique identifier for the user
Returns
Promise<boolean>: Resolves totrueif SDK was started successfully,falseotherwise
stopPoilabsAnalysis()
Stops the Poilabs Analysis SDK.
Returns
boolean:trueif SDK was stopped successfully,falseotherwise
updateUniqueId(uniqueId)
Updates the unique identifier in the SDK after initialization.
Parameters
uniqueId(String): New unique identifier for the user
Returns
Promise<boolean>: Resolves totrueif update was successful
configureAnalysisSDK(options)
Configures the Poilabs Analysis SDK with advanced options including foreground service for doze mode prevention.
Parameters
options(Object):enabled(boolean): Enable/disable the SDKenableForegroundService(boolean): Enable foreground service to prevent doze mode (Android only)serviceNotificationTitle(string): Title for the foreground service notificationnotificationChannelName(string): Name for the notification channelnotificationChannelDescription(string): Description for the notification channelnotificationIconResourceId(number): Custom icon for the notification
Returns
Promise<boolean>: Resolves totrueif configuration was successful
Example
// Enable foreground service for doze mode prevention
await configureAnalysisSDK({
enabled: true,
enableForegroundService: true,
serviceNotificationTitle: 'Poilabs SDK Active',
notificationChannelName: 'Poilabs SDK Service',
notificationChannelDescription: 'Keeps Poilabs SDK running during doze mode'
});requestRequiredPermissions()
Requests all the required permissions for the SDK to work properly.
Returns
Promise<boolean>: Resolves totrueif all required permissions are granted,falseotherwise
checkAllPermissions()
Checks if all required permissions are granted.
Returns
Promise<boolean>:trueif all required permissions are granted,falseotherwise
checkBluetoothPermission()
Checks if Bluetooth permissions are granted (relevant for Android 12+).
Returns
Promise<boolean>:trueif Bluetooth permissions are granted,falseotherwise
📋 Required Permissions
The plugin automatically adds these permissions:
Android
INTERNET- For network communicationACCESS_FINE_LOCATION- For precise locationACCESS_COARSE_LOCATION- For approximate location (Android 9 and below)ACCESS_BACKGROUND_LOCATION- For background location tracking (Android 10+)BLUETOOTH_CONNECT- For Bluetooth connectivity (Android 12+)BLUETOOTH_SCAN- For Bluetooth scanning (Android 12+)FOREGROUND_SERVICEwithforegroundServiceType="location"- For background operationsREQUEST_IGNORE_BATTERY_OPTIMIZATIONS- For preventing doze mode from stopping the SDK
iOS
NSLocationWhenInUseUsageDescription- Location permission when app is in useNSLocationAlwaysUsageDescription- Location permission even when app is not in useNSBluetoothAlwaysUsageDescription- Bluetooth permission
❓ Troubleshooting
Module not found error
If you see PoilabsAnalysisModule not found error:
- Make sure you have run
npx expo prebuild - Verify you've completed the additional setup steps for Android/iOS
- Run
npx expo run:androidornpx expo run:iosto build and run the native project - For Expo Go, this plugin will not work because it requires native modules
iOS Integration Issues
If you're having issues with iOS integration:
- Make sure the Podfile is correctly updated with
pod 'PoilabsAnalysis' - Verify that
use_frameworks! :linkage => :staticis in your Podfile - Check that the Swift files are properly added to your project
- Run
pod install --repo-updatefrom the ios directory
Permission issues
If the SDK is not working due to permission issues:
- Make sure you have requested all the necessary permissions
- For Android 10+, background location permission needs to be requested separately
🔧 For Poilabs Developers: SDK Version Management
This plugin uses centralized SDK version management through sdk-versions.json.
Setup Environment Variables
For automatic version fetching from private repositories, create a .env file:
# Copy the example file
cp env.example .env
# Edit with your tokens
# JITPACK_TOKEN=your_actual_jitpack_token_hereNote: The .env file is ignored by git for security.
Current SDK Versions
Check current versions:
npm run dev:show-versionsUpdating SDK Versions
Manual version update:
npm run dev:update-sdk-versions -- --android v3.12.0
npm run dev:update-sdk-versions -- --ios 1.2.0
npm run dev:update-sdk-versions -- --android v3.12.0 --ios 1.2.0Fetch latest versions automatically:
# Fetch latest versions for both platforms (uses .env file)
npm run dev:fetch-latest-versions
# Override with command line token
npm run dev:update-sdk-versions -- --fetch-latest --token YOUR_JITPACK_TOKEN
# Fetch latest for specific platform
npm run dev:update-sdk-versions -- --android latest
npm run dev:update-sdk-versions -- --ios latestRelease Process
- Test new SDK versions with current plugin
- Update versions using the script above
- Test thoroughly with updated versions
- Update plugin version in package.json
- Publish to npm:
npm publish
CI/CD Integration
See .github/workflows/update-sdk.yml.example for automated SDK version management workflow.
📞 Support
If you encounter any issues, please contact Poilabs support or open an issue on GitHub.
