iOS setup
Seven steps, in order. Three of them fail silently when missed — the app builds, runs, and looks correct while protecting nothing — and those three are marked below.
Three of these steps fail silently when missed: the app builds, runs, and looks correct while protecting nothing. They are marked below.
-
cd ios && pod install -
Keychain sharing — this is for the SDK, not for your app's data. The two Microsoft groups exist because token and policy state is shared between separate processes: the broker (Authenticator / Company Portal) writes a token and the SDK inside your process reads it. iOS isolates the keychain per app, so an access group is the only way that hand-off works.
You are free to store your own data however you like — MMKV, files, SQLite. This step does not constrain that.
Enable the Keychain Sharing capability and set the access groups in this order:
$(AppIdentifierPrefix)<your.bundle.id> ← must be first$(AppIdentifierPrefix)com.microsoft.intune.mam$(AppIdentifierPrefix)com.microsoft.adalcacheYour bundle ID must be first: without an explicit access group, iOS writes to the first group in the entitlements, so a Microsoft group in that position would receive any keychain items your app does write.
com.microsoft.adalcacheis MSAL's token cache;com.microsoft.intune.mamis where the SDK keeps enrollment state and policy.Your provisioning profile's
keychain-access-groupsmust contain a wildcard (YOURBUNDLESEEDID.*) — the Microsoft groups don't carry your prefix, so without it the entitlements won't sign. If your app already uses a custom MSAL keychain group, use that instead ofcom.microsoft.adalcacheand setADALCacheKeychainGroupOverrideto match.Miss
com.microsoft.adalcacheand the broker acquires a token the SDK cannot see: enrollment fails with "could not access the user's AAD token" even though sign-in succeeded. -
Run
IntuneMAMConfiguratoras a build phase. It writes the requiredInfo.plistkeys and entitlements, including a long and changing list ofLSApplicationQueriesSchemesentries:"$SRCROOT/../node_modules/react-native-intune/vendor/ios/IntuneMAMConfigurator" \-i "$SRCROOT/YourApp/Info.plist" \-e "$SRCROOT/YourApp/YourApp.entitlements"It is idempotent. Re-run it whenever your plist, entitlements, or the SDK version change — make it a build phase rather than a one-time manual step.
-
Build settings:
STRIP_SWIFT_SYMBOLS = NO,ENABLE_BITCODE = NO. If you use Xcode 26+ "Enhanced Security", disable Authenticate pointers and Enable Read-only Platform Memory. -
Do not add
ADALClientId,ADALAuthorityorADALRedirectUritoInfo.plist. This library configures identity at runtime. A conflicting plist key alongside a runtime override is a known cause of sign-in timeouts and error 53009. -
SwiftUI apps: ensure
UIApplicationSceneManifest→UISceneConfigurationsis present and non-empty. If it is missing, the SDK will not protect your app even when policy applies successfully. -
MaxFileProtectionLevel, if your app reads its own files while the screen is locked. The SDK's default isNSFileProtectionComplete, which makes protected files unreadable roughly ten seconds after the device locks — enough to break a local database, a background sync, or anything on a timer.<key>IntuneMAMSettings</key><dict><key>MaxFileProtectionLevel</key><string>NSFileProtectionCompleteUntilFirstUserAuthentication</string></dict>This key has no runtime equivalent — the SDK reads it at launch. Passing
maxFileProtectionLeveltoconfigure()without the matching plist key is therefore rejected withE_PLIST_CONFLICTrather than silently ignored, because an option that appears to be set and is not is how an app ships with an unreadable database. The Expo plugin writes the key for you from themaxFileProtectionLevelprop. -
Embed settings: select Embed & Sign for the vendored frameworks in your app target, and Do Not Embed for any extensions.