Installation
Detailed installation instructions for bare React Native and Expo projects.
Requirements#
- • React Native 0.72 or later
- • iOS 13.4+ / Android 6.0+ (API 23+)
- • For Expo: SDK 50+ (Expo Go not supported)
Bare React Native#
Install Packages#
Install the SDK and required peer dependencies:
Attribution + Conversions:
npm install @mobana/react-native-sdk \
@react-native-async-storage/async-storageWith Flows:
npm install @mobana/react-native-sdk \
@react-native-async-storage/async-storage \
react-native-webviewOptional Flow Enhancements:
# Optional packages for additional Flow capabilities
npm install react-native-haptic-feedback \
react-native-permissions \
react-native-in-app-review \
react-native-geolocation-service \
react-native-safe-area-context| Package | Purpose |
|---|---|
react-native-haptic-feedback | Haptic feedback in flows |
react-native-permissions | Permission prompts in flows (notifications, location, ATT) |
react-native-in-app-review | App store review prompts |
react-native-geolocation-service | Location services in flows |
react-native-safe-area-context | Safe area insets (recommended) |
iOS Setup (optional, for flows with permissions)#
Only needed if you use react-native-permissions (flows that request notifications, location, or ATT). Configure permissions in your Podfile. Only include permissions for features your flows actually use. Attribution-only and flows without permission prompts don't need this.
# In ios/Podfile, add before target block:
def node_require(script)
require Pod::Executable.execute_command('node', ['-p',
"require.resolve('#{script}', {paths: [process.argv[1]]})",
__dir__]).strip
end
node_require('react-native-permissions/scripts/setup.rb')
# Only include permissions you actually need:
setup_permissions([
'Notifications', # For notification permission prompts
'AppTrackingTransparency', # For ATT prompts (iOS 14.5+)
'LocationWhenInUse', # For foreground location flows
'LocationAlways', # For background location flows
])Then install pods:
cd ios && pod installYou also need to add usage description strings to Info.plist for each permission your flows request. Apple requires these — without them the app will crash when a permission is requested. Customize the text to describe your app's actual use:
<!-- ios/YourApp/Info.plist — add entries for permissions your flows use -->
<!-- Required for ATT prompts (AppTrackingTransparency) -->
<key>NSUserTrackingUsageDescription</key>
<string>We use this identifier to show you relevant content and measure ad effectiveness.</string>
<!-- Required for foreground location (LocationWhenInUse) -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to provide location-relevant features.</string>
<!-- Required for background location (LocationAlways) -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location in the background to provide location-relevant features.</string>Android Setup#
Add permissions to your AndroidManifest.xml. Only include permissions for features your flows actually use.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- For notification permission prompts (Android 13+) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- For foreground location flows -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- For background location flows -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- ... rest of your manifest -->
</manifest>Expo#
This SDK requires native code. Expo Go is not supported. Use expo-dev-client for development builds.
Install Packages#
Attribution + Conversions:
npx expo install @mobana/react-native-sdk \
@react-native-async-storage/async-storageWith Flows:
npx expo install @mobana/react-native-sdk \
@react-native-async-storage/async-storage \
react-native-webviewOptional Flow Enhancements:
npx expo install react-native-haptic-feedback \
react-native-permissions \
react-native-in-app-review \
react-native-geolocation-service \
react-native-safe-area-contextConfigure Expo#
Add the plugin to your app.json or app.config.js:
Attribution-only (no special permissions):
{
"expo": {
"plugins": ["@mobana/react-native-sdk"]
}
}With Flows (add permissions you need):
If your flows use permission prompts, you must install react-native-permissions (npx expo install react-native-permissions). The Mobana plugin handles the native setup (Podfile, Info.plist, AndroidManifest) automatically, but the package itself is still needed at runtime.
{
"expo": {
"plugins": [
["@mobana/react-native-sdk", {
"permissions": [
"Notifications",
"AppTrackingTransparency",
"LocationWhenInUse",
"LocationAlways"
]
}]
]
}
}| Permission | Use Case |
|---|---|
Notifications | Push notification prompts |
AppTrackingTransparency | ATT prompts (iOS 14.5+) |
LocationWhenInUse | Foreground location |
LocationAlways | Background location |
iOS Usage Description Strings#
The Mobana plugin automatically adds default usage description strings to Info.plist for each permission you enable. However, the defaults are generic — Apple may reject apps with vague ATT strings in particular. Always override them with copy that describes your app's actual use via expo.ios.infoPlist in app.config.js:
{
"expo": {
"ios": {
"infoPlist": {
"NSUserTrackingUsageDescription": "We use this identifier to show you relevant content and measure ad effectiveness.",
"NSLocationWhenInUseUsageDescription": "We use your location to provide location-relevant features.",
"NSLocationAlwaysAndWhenInUseUsageDescription": "We use your location in the background to provide location-relevant features."
}
},
"plugins": [
["@mobana/react-native-sdk", {
"permissions": ["AppTrackingTransparency", "LocationWhenInUse"]
}]
]
}
}Apple reviews NSUserTrackingUsageDescription closely. The plugin default is a reasonable starting point, but always customize it to describe how your specific app uses the identifier.
Plugin defaults
NSUserTrackingUsageDescription(AppTrackingTransparency)"This identifier will be used to measure effectiveness of our campaigns and deliver relevant content to you."
NSLocationWhenInUseUsageDescription(LocationWhenInUse / LocationAlways)"This app needs access to your location."
NSLocationAlwaysAndWhenInUseUsageDescription(LocationAlways)"This app needs access to your location in the background."
Deeplinks (optional)#
Required only if you want Mobana links to open your app directly when it's installed. Skip this section if you only need install attribution.
Mobana uses two complementary mechanisms. The primary path fires your iOS URL Scheme (myapp://) or an Android intent:// URL from the redirect page. The optional enhancement is Universal Links / App Links — the OS intercepts the URL before the browser opens, giving a slightly faster tap-to-open with no browser tab left behind.
Add your iOS URL Scheme (primary) and optionally your iOS Team ID and Android SHA-256 fingerprint(s) in Dashboard → App Settings → Deeplinks. If you want to enable Universal Links, add your Team ID before shipping a build with the applinks: entitlement — Apple caches a missing AASA for up to 7 days. See the Deeplinks guide for details.
iOS URL Scheme (required for direct opening)#
Register the same scheme you entered in the Dashboard (e.g. myapp) in your app. When a Mobana link is tapped, the redirect page fires myapp://… and iOS opens your app directly.
Bare React Native — add to ios/YourApp/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>Expo — add scheme to app.json:
{
"expo": {
"scheme": "myapp"
}
}Bare React Native — Associated Domains (Universal Links, optional)#
To enable Universal Links (OS-level interception before the browser opens), add an Associated Domains entitlement. Requires your iOS Team ID to be set in App Settings → Deeplinks first.
Open your project in Xcode → select your app target → Signing & Capabilities → click + Capability → Associated Domains. Add an entry for your Mobana host:
# Xcode → your target → Signing & Capabilities → + Capability → Associated Domains
# Add this entry (replace YOUR_APP_ID with your Mobana app ID):
applinks:YOUR_APP_ID.mobana.ai
# For dev builds, append ?mode=developer so iOS bypasses Apple's CDN cache
# and fetches AASA directly from your host on every install. Remove for
# production / TestFlight / App Store builds:
applinks:YOUR_APP_ID.mobana.ai?mode=developer
# If you also use a custom endpoint (Mobana custom domain), add:
applinks:yourdomain.comOn debug/dev-signed builds, append ?mode=developer to the applinks: entry. iOS will fetch AASA directly from your server on every (re)install instead of going through Apple's CDN — eliminating the 7-day cache wait when iterating on setup. Apple ignores this flag on TestFlight / App Store builds, so remove it before submitting.
Bare React Native — Android intent-filter (required)#
Add an <intent-filter> to your launcher activity in AndroidManifest.xml. This lets the Android Linking API receive the URL from Mobana's intent:// redirect. The android:pathPrefix="/deep" is critical — without it, Android may route taps on /find, /conversion, and other Mobana paths through your app instead of the browser. android:autoVerify="true" is included by default — it is safe without fingerprints (falls back gracefully) and automatically enables App Links once you configure SHA-256 fingerprints in App Settings → Deeplinks.
<!-- AndroidManifest.xml — inside your launcher <activity> -->
<!-- autoVerify enables App Links (OS-level interception) once you add
SHA-256 fingerprints in App Settings → Deeplinks -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="YOUR_APP_ID.mobana.ai"
android:pathPrefix="/deep" />
</intent-filter>
<!-- Custom endpoint — add this block ONLY if you use a Mobana custom domain.
Each host needs its own <intent-filter>; mixing hosts in one filter
creates unintended host × path combinations. -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="yourdomain.com"
android:pathPrefix="/da/deep" />
</intent-filter>Expo#
The Mobana config plugin handles permission native setup but does not auto-configure the URL Scheme, Associated Domains, or App Links intent filters — these depend on your appId and optional custom domain. Add scheme (required for iOS direct opening) plus expo.ios.associatedDomains and expo.android.intentFilters in app.json / app.config.js alongside the plugin:
{
"expo": {
"scheme": "myapp",
"ios": {
"associatedDomains": [
"applinks:YOUR_APP_ID.mobana.ai"
]
},
"android": {
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "YOUR_APP_ID.mobana.ai",
"pathPrefix": "/deep"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
},
"plugins": ["@mobana/react-native-sdk"]
}
}Dev builds: bypass AASA cache (Universal Links only)
If you're using Universal Links (optional), switch to app.config.js so you can conditionally append ?mode=developer to the applinks: entry. iOS then fetches AASA directly from your server on each install — useful while iterating on Team ID setup. Not needed if you're only using the iOS URL Scheme path.
// app.config.js — use ?mode=developer in dev builds so iOS bypasses
// Apple's AASA CDN cache. Strip it for production builds.
const IS_DEV = process.env.APP_VARIANT === "development";
export default {
expo: {
ios: {
associatedDomains: [
IS_DEV
? "applinks:YOUR_APP_ID.mobana.ai?mode=developer"
: "applinks:YOUR_APP_ID.mobana.ai",
],
},
android: {
intentFilters: [
{
action: "VIEW",
autoVerify: true,
data: [
{
scheme: "https",
host: "YOUR_APP_ID.mobana.ai",
pathPrefix: "/deep",
},
],
category: ["BROWSABLE", "DEFAULT"],
},
],
},
plugins: ["@mobana/react-native-sdk"],
},
};After editing, regenerate the native projects with npx expo prebuild --clean.
Runtime wiring#
Once native config is in place, subscribe to Mobana link payloads with onDeepLink(). It fires for every delivery case — Universal Link, deferred first-launch, and probe re-engagement — so one handler covers all paths.
import { Mobana } from '@mobana/react-native-sdk';
interface UnlockData { unlock?: string; ref?: string }
Mobana.onDeepLink<UnlockData>((event) => {
if (event.data?.unlock) unlockPromo(event.data.unlock);
});See the Deeplinks guide for the full delivery matrix and the onDeepLink() reference for the event shape.
Verify Installation#
Quick check that the SDK initializes correctly:
import { Mobana } from '@mobana/react-native-sdk';
Mobana.init({
appId: 'YOUR_APP_ID',
appKey: 'YOUR_APP_KEY',
debug: true, // Enable logging
});
// You should see "Mobana initialized" in your consoleFor complete testing of attribution, conversions, and flows, see the Test Setup Guide.