Base on this here is a tutorial to use PoseTracker in a Mobile App :
News — PoseTracker (iOS): camera permissions and WebView behavior
This covers:
Request camera access correctly on iOS with expo-camera.
Avoid the extra WebView popup on iOS 15+: “Allow app.posetracker.com to access the camera”.
Stay compliant with App Store guideline 5.1.1.
1) Native camera permission (required)
PoseTracker runs inside a WebView.
iOS blocks camera usage until the native app has camera permission.
We use expo-camera:
useCameraPermissions() to check / request access.
NSCameraUsageDescription in app.json → ios.infoPlist.
Example:
{"expo":{"ios":{"infoPlist":{"NSCameraUsageDescription":"Camera access is required for real-time tracking."}}}}
Recommended flow (used below):
Don’t request camera access on app launch.
Request it only when the user starts a camera feature (ex: “Live camera”).
If access is denied, explain why.
Show “Open Settings” only when the user tries again.
2) WebView extra camera prompt on iOS 15+
Even when native permission is granted, iOS may show a second prompt inside the WebView.
To avoid it on iOS 15+, set mediaCapturePermissionGrantType="grant" on react-native-webview.
Conditions:
iOS 15+.
Native camera permission is already granted.
Result:
No extra “Allow … to access the camera” prompt on each WebView mount.
You can unmount/remount without re-prompting.
3) App Store guideline 5.1.1
To stay compliant:
Don’t redirect to Settings before showing the system permission dialog.
Request permission only when the user starts a camera feature.
If they deny and try again, you can show an explanation and an optional Settings link.
Start a new Expo project
Install dependencies for the webview
Start and run the project on your Phone or a Simulator
For this part we recommend to use your phone with Expo Go App as described in Expo Documentation. Because we will need to access a camera with a human to track.
Integrate PoseTracker
Define your App.js and set your API_KEY :
How does it works ?
First we have the WebView :
That use posetracker_url who's build with PoseTracker params.
🟧 Important point : Data exchange between PoseTracker and your Application 🟧
We use what you are sending throw injectedJavaScript to send back data to our App. So we create a bridge between PoseTracker API frontend and YOUR actual App with :
Then we use your bridge to send you some information, and here we handle it with:
<WebView
javaScriptEnabled={true}
domStorageEnabled={true}
allowsInlineMediaPlayback={true}
mediaPlaybackRequiresUserAction={false}
style={styles.webView}
source={{ uri: posetracker_url }}
originWhitelist={['*']}
injectedJavaScript={jsBridge}
onMessage={onMessage}
{...(Platform.OS === "ios" && {
mediaCapturePermissionGrantType: "grant",
})}
// Activer le debug pour voir les logs WebView
debuggingEnabled={true}
// Permettre les communications mixtes HTTP/HTTPS si nécessaire
mixedContentMode="compatibility"
// Ajouter un gestionnaire d'erreurs
onError={(syntheticEvent) => {
const { nativeEvent } = syntheticEvent;
console.warn('WebView error:', nativeEvent);
}}
// Ajouter un gestionnaire pour les erreurs de chargement
onLoadingError={(syntheticEvent) => {
const { nativeEvent } = syntheticEvent;
console.warn('WebView loading error:', nativeEvent);
}}
/>
injectedJavaScript={jsBridge}
// This is a basic js bridge
// We need a bridge to transit data between the ReactNative app and our WebView
// The WebView will use this function define here to send info that we will use later
const jsBridge = `
window.addEventListener('message', function(event) {
window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
});
window.webViewCallback = function(data) {
window.ReactNativeWebView.postMessage(JSON.stringify(data));
};
const originalPostMessage = window.postMessage;
window.postMessage = function(data) {
window.ReactNativeWebView.postMessage(typeof data === 'string' ? data : JSON.stringify(data));
};
true; // Important for a correct injection
`;