Official Expo Config Plugin for integrating the Poilabs Analysis SDK
Official Expo Config Plugin for integrating the Poilabs Analysis SDK into Expo (prebuild) projects.
> š Automatically links native dependencies and modifies required iOS/Android files.
---
When used with expo prebuild, this plugin:
- ā
Android Permissions: Adds all required permissions to AndroidManifest.xml
- Including REQUEST_IGNORE_BATTERY_OPTIMIZATIONS for doze mode prevention
- Adds android:foregroundServiceType="location" to the FOREGROUND_SERVICE permission
- ā
Android Native Integration: Automatically configures native modules
- Copies and registers PoilabsPackage in MainApplication.kt
- Adds Poilabs SDK dependency to android/app/build.gradle
- Configures JitPack repository in android/build.gradle
- ā
iOS Integration:
- Adds pod 'PoilabsAnalysis' to the iOS Podfile
- Adds Info.plist keys for Location and Bluetooth usage
- ā
Foreground Service Setup: Automatically configures foreground service intent for doze mode prevention
---
Install the plugin to your Expo project:
``bash`
npm install @poilabs/analysis-sdk-pluginor
yarn add @poilabs/analysis-sdk-plugin
Also install the required dependencies:
`bash`
npx expo install expo-location expo-device
Add the plugin to your app.json or app.config.js:
`json`
{
"expo": {
"plugins": [
[
"@poilabs/analysis-sdk-plugin",
{
"jitpackToken": "YOUR_JITPACK_TOKEN" // Get this from Poilabs
}
]
]
}
}
Then run the prebuild command:
`bash`
npx expo prebuild
The plugin now automatically handles Android setup:
ā
Auto-registers PoilabsPackage in MainApplication.kt REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
ā
Auto-adds permission
ā
Auto-configures foreground service intent
Simply run:
`bash`
npx expo prebuild
npx expo run:android
That's it! No manual code changes needed.
- 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:
1. Open your Xcode project
2. In Xcode, verify that the PoilabsModule files are added to your project
3. Check that the files appear in the "Build Phases > Compile Sources" section
4. Find + button and click. Then you should "add other".
5. If files are missing, you may need to manually add them from the iOS/
- PoilabsAnalysisModule.h
- PoilabsAnalysisModule.m
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:
`bash`
npx expo run:ios
After the prebuild process, you can use the SDK in your application:
`javascript
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 (
headerImage={
style={styles.reactLogo}
/>
}
>
{/ SDK Status Indicator /}
{/ Stop SDK Button /}
);
}
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",
},
});
`
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:
`javascript
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_OPTIMIZATIONS permission 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
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.
`javascript
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 (
{events.map((event, index) => (
))}
);
}
`
#### onBeaconDetected
Emitted when a beacon is detected by the SDK.
Event Data:
`typescript`
{
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:
`typescript`
{
success: boolean;
data?: any;
error?: string;
timestamp: number;
source: string;
[key: string]: any;
}
#### onError
Emitted when the SDK encounters an error.
Event Data:
`typescript`
{
code: number;
message: string;
domain: string;
timestamp: number;
source: string;
}
#### onStatusUpdate
Emitted when the SDK status changes.
Event Data:
`typescript`
{
status: 'started' | 'stopped' | 'scanning' | 'idle' | 'error' | 'unknown';
message?: string;
timestamp: number;
source: string;
}
#### 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:
`javascript
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:
`javascript
// Remove all listeners for beacon detection
removeAllMonitoringListeners('onBeaconDetected');
// Remove all listeners for all event types
removeAllMonitoringListeners();
`
#### removeAllListeners()
Removes all monitoring event listeners.
Example:
`javascript`
removeAllListeners();
For debugging purposes, you can test if the event system is working correctly:
`javascript
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');
}
`
- iOS Only: Monitoring events are currently only available on iOS. Android returns false with warnings.startPoilabsAnalysis()
- 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 to ensure you don't miss any events.
- Memory Management: Always cleanup listeners when component unmounts to prevent memory leaks.
`javascript
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 (
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
{new Date(item.data.timestamp).toLocaleTimeString()}
{JSON.stringify(item.data, null, 2)}
)}
ListEmptyComponent={
No events yet. Get near beacons to see activity.
}
/>
);
}
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 }
});
`
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: Resolves to true if SDK was started successfully, false otherwise
Stops the Poilabs Analysis SDK.
#### Returns
- boolean: true if SDK was stopped successfully, false otherwise
Updates the unique identifier in the SDK after initialization.
#### Parameters
- uniqueId (String): New unique identifier for the user
#### Returns
- Promise: Resolves to true if update was successful
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: Resolves to true if configuration was successful
#### Example
`javascript`
// 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'
});
Requests all the required permissions for the SDK to work properly.
#### Returns
- Promise: Resolves to true if all required permissions are granted, false otherwise
Checks if all required permissions are granted.
#### Returns
- Promise: true if all required permissions are granted, false otherwise
Checks if Bluetooth permissions are granted (relevant for Android 12+).
#### Returns
- Promise: true if Bluetooth permissions are granted, false otherwise
The plugin automatically adds these permissions:
- 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_SERVICE
- with foregroundServiceType="location" - For background operationsREQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- - For preventing doze mode from stopping the SDK
- NSLocationWhenInUseUsageDescription - Location permission when app is in useNSLocationAlwaysUsageDescription
- - Location permission even when app is not in useNSBluetoothAlwaysUsageDescription
- - Bluetooth permission
If you see PoilabsAnalysisModule not found error:
1. Make sure you have run npx expo prebuildnpx expo run:android
2. Verify you've completed the additional setup steps for Android/iOS
3. Run or npx expo run:ios to build and run the native project
4. For Expo Go, this plugin will not work because it requires native modules
If you're having issues with iOS integration:
1. Make sure the Podfile is correctly updated with pod 'PoilabsAnalysis'use_frameworks! :linkage => :static
2. Verify that is in your Podfilepod install --repo-update
3. Check that the Swift files are properly added to your project
4. Run from the ios directory
If the SDK is not working due to permission issues:
1. Make sure you have requested all the necessary permissions
2. For Android 10+, background location permission needs to be requested separately
This plugin uses centralized SDK version management through sdk-versions.json.
For automatic version fetching from private repositories, create a .env file:
`bashCopy the example file
cp env.example .env
Note: The
.env file is ignored by git for security.$3
Check current versions:
`bash
npm run dev:show-versions
`$3
Manual version update:
`bash
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.0
`Fetch latest versions automatically:
`bash
Fetch latest versions for both platforms (uses .env file)
npm run dev:fetch-latest-versionsOverride with command line token
npm run dev:update-sdk-versions -- --fetch-latest --token YOUR_JITPACK_TOKENFetch latest for specific platform
npm run dev:update-sdk-versions -- --android latest
npm run dev:update-sdk-versions -- --ios latest
`$3
1. Test new SDK versions with current plugin
2. Update versions using the script above
3. Test thoroughly with updated versions
4. Update plugin version in package.json
5. Publish to npm:
npm publish$3
See
.github/workflows/update-sdk.yml.example` for automated SDK version management workflow.If you encounter any issues, please contact Poilabs support or open an issue on GitHub.