Requirements
The SDK is a native module, so it does not run in Expo Go. A custom dev client or a bare
React Native project is required.
Install the package
pod entry, a post_install hook, a settings.gradle include, or a MainApplication
registration. Pre-built native libraries ship in the package for both platforms.
pod install should list MeshSdk under “Auto-linking React Native modules”. The iOS native
binary ships as an XCFramework, so CocoaPods picks the right slice per build SDK and device
and simulator builds both work with no linker configuration.
iOS setup
Info.plist
bluetooth-central is
declared.
For the optional Wi-Fi peer-stream transport, also add the keys in phone Wi-Fi peer streams.
Android setup
What the SDK already declares
The SDK ships its ownAndroidManifest.xml, and these entries merge into your app
automatically, so you do not need to copy them:
neverForLocation flags on BLUETOOTH_SCAN and NEARBY_WIFI_DEVICES and the
maxSdkVersion="30" gating propagate into your merged manifest. neverForLocation has Play
Console data-safety implications you inherit, so it is worth knowing it is there. If your manifest
declares NEARBY_WIFI_DEVICES without neverForLocation, yours wins the merge and Wi-Fi Direct
needs a location grant on Android 13 and later.
What your app must declare
The SDK deliberately does not force these on consumers, but the code requires them:ProGuard / R8
Nothing to configure. The SDK shipsconsumerProguardFiles, so its keep rules are applied to
your release build automatically. Without them, release builds would silently lose the BLE
mesh transport while debug builds kept working, but the rules are already handled for you.
Runtime permissions
Android
The SDK checks permissions but never requests them. Your app must request them before callingstart(), or the SDK emits a diagnostic event at error level with “Missing Bluetooth
permissions”.
BLUETOOTH and BLUETOOTH_ADMIN are install-time permissions on API 30 and below, so they
do not need a runtime request.
You can also check and prompt for Bluetooth itself:
iOS
No runtime API call is needed. The system prompts on first CoreBluetooth use, driven by the Info.plist usage descriptions.Phone Wi-Fi peer streams
ThewifiDirect transport is optional and off by default. From v0.28.0 it carries traffic on
both platforms: each side proves its address with an identity preamble before anything reaches
the protocol. wifiDirect: { enabled: true } starts it from start().
An iPhone and an Android phone meet over this slot only on a shared Wi-Fi network that allows
multicast and does not isolate clients; an iPhone cannot join a Wi-Fi Direct group. With no
shared network they meet over Bluetooth LE or the relay. A Wi-Fi network with no internet counts.
An Android phone on 0.28 or earlier does not advertise on the LAN. An iPhone on 0.28.0 or later
does not see one on 0.27 or earlier over this slot, because 0.27 used MultipeerConnectivity. See upgrade from v0.27.
Android local network permission
The Wi-Fi network path needs no runtime grant while your app targets Android 16 (API 36) or lower, so it runs on a phone that has not grantedNEARBY_WIFI_DEVICES; Wi-Fi Direct then stays
off until enableTransport('wifiDirect') after the grant. When the app targets Android 17
(API 37), declare ACCESS_LOCAL_NETWORK and request it before start(), or the LAN carrier stays
off with a warning diagnostic. The SDK does not declare it, because a declaration revokes the grant
that apps targeting 36 and lower hold by default.
wifiDirect
off to stop both.
iOS keys
List the service type and describe local-network use inInfo.plist, or iOS local-network
privacy blocks discovery. The first start() with the transport enabled shows the Local Network
prompt, and a denial is reported as an error diagnostic.
Android group formation
WithwifiDirect: { enabled: true, autoAccept: true } on Android 10 and later, devices of the
same app find each other over Wi-Fi P2P service discovery and join one group, with no system
dialog on either phone. Expect one to two minutes: two phones took 18 to 225 seconds in the SDK’s
device tests, and Bluetooth LE carries traffic meanwhile. Groups of three or more devices are
untested. Without autoAccept, pair the phones once in the system’s Wi-Fi Direct settings.
groupOwnerIntent is deprecated and unused.
The group’s name and passphrase are derived from your mesh app identifier, so they keep apps
apart but are not a secret; the identity preamble and end-to-end encryption still protect the
traffic. Request NEARBY_WIFI_DEVICES at run time on Android 13 and later, and
ACCESS_FINE_LOCATION on Android 12 and lower, before start(). An enable refused for a
missing grant is logged and not retried; call enableTransport('wifiDirect') after a later
grant. On Android 12 and lower, make sure your ACCESS_FINE_LOCATION declaration also covers
API 31 and 32, which the BLE-only entry above does not.
Optional: Android mesh wake
This additional native configuration enables recovery after Android kills your process. Add to your app’sAndroidManifest.xml, inside
<application>:
index.js, not inside a component:
mesh-state is application-owned code: it stores the process singleton, saved configuration, durable receive handler and provisioned relay credentials. Implement those functions before opting in. The example shows their required integration points, not a shipped SDK module.
Both the manifest opt-in and module-scope registration are required. The application must:
- Attach a durable receive handler before
start(). Core acknowledgment precedes the event and is not proof of persistence. - Return promptly if an instance already exists; never create a second protocol for the process.
- Restore configuration and transports, including relay credentials passed through
enableTransport. - Resolve within the task budget (60 seconds by default, configurable from 10 to 300 seconds). Do not run a permanent loop in the task.
Mesh wake requires React Native 0.76.5+ when the New Architecture is enabled. Headless tasks
did not work under bridgeless before 0.76 and were patchy until 0.76.5. On RN 0.84 and 0.85 a
core bug (fixed in 0.86) can leave the wake service running after the task finishes.
Troubleshooting
Linking error
If you see the “doesn’t seem to be linked” error:- Run
pod install(iOS) and rebuild natively. A JS-only reload will not pick up native changes. - Confirm
pod installoutput listsMeshSdkunder “Auto-linking React Native modules”. - Run
npx react-native configand checkplatforms.ios.podspecPathends inMeshSdk.podspec. - Remove any leftover manual
pod 'MeshSdk'line; it can shadow the autolinked pod. - Verify you are not using Expo Go.
Simulator link failures
Undefined symbols, or “building for iOS Simulator, but linking in object file built for
iOS”, means a leftover manual post_install hook is still linking archives by hand. Remove
it per the upgrade warning above.
For upgrades from earlier releases, see Upgrade the SDK.
Next steps
Set up the SDK in your app

