Skip to content

fix(ios): CoreLocation import + wrap TurboModule constants as callable functions#357

Merged
zaferatli merged 8 commits into
GantMan:masterfrom
hbabathe:fix/ios-corelocation-import
Apr 27, 2026
Merged

fix(ios): CoreLocation import + wrap TurboModule constants as callable functions#357
zaferatli merged 8 commits into
GantMan:masterfrom
hbabathe:fix/ios-corelocation-import

Conversation

@hbabathe

@hbabathe hbabathe commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • CoreLocation import: Adds missing #import <CoreLocation/CoreLocation.h> needed for new architecture builds
  • TurboModule constant wrapping: The native iOS module exports isJailBroken, canMockLocation, etc. via constantsToExport as plain booleans. The JS entry point only wrapped these into functions for the legacy NativeModules path — when the TurboModule loaded on new-arch builds, callers got raw booleans instead of functions, causing "isJailBroken is not a function" runtime crashes. Now wrapping is applied uniformly regardless of module source.
  • TurboModule detection: Replaced unreliable global.__turboModuleProxy check with a try/catch require of the TurboModule spec
  • ExampleExpo55 updates: Tarball-based install via npm pack, build scripts, SafeAreaContext fix, entry point corrected to .tsx
  • Housekeeping: Updated .gitignore/.npmignore to exclude artifacts/ and ExampleExpo55/

Test plan

  • Verified on iOS (Expo SDK 55, new architecture) — JailMonkey.isJailBroken() returns a boolean instead of crashing
  • Verify on Android new-arch build
  • Verify legacy (old arch) still works

CLLocationManager/CLLocation are used for mock-location detection on
iOS 15+ without importing CoreLocation, causing compile failures when
RCT_NEW_ARCH_ENABLED is set (GantMan#354).

Link CoreLocation in the podspec so the framework is available at link time.

Made-with: Cursor
Expo 55 / RN 0.83 dev-client example with local jail-monkey via file:..
Postinstall copies library files so iOS autolinking sees a real podspec.
Add install-example-expo55 script at repo root.
The native iOS module exports isJailBroken, canMockLocation, etc. via
constantsToExport as plain booleans. The old JS entry point only wrapped
these into functions for the legacy NativeModules path — when the
TurboModule loaded successfully, callers got raw booleans, causing
"isJailBroken is not a function" crashes on new-arch builds.

Now the wrapping is applied uniformly regardless of module source.

Also updates ExampleExpo55 tooling: tarball-based install, build
scripts, and removes deleted android source files.
@hbabathe hbabathe changed the title fix(ios): add CoreLocation import for new architecture build fix(ios): CoreLocation import + wrap TurboModule constants as callable functions Mar 30, 2026
@hbabathe hbabathe marked this pull request as ready for review March 30, 2026 07:18
@hbabathe hbabathe marked this pull request as draft March 30, 2026 07:20
Accidentally included android/ deletions from working tree in the
previous commit. These files are the entire Android native implementation.
@hbabathe hbabathe marked this pull request as ready for review March 30, 2026 07:33
@xxxifan

xxxifan commented Apr 17, 2026

Copy link
Copy Markdown

please merge this

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves iOS New Architecture compatibility by ensuring CoreLocation is linked/imported and by normalizing the JS API surface so TurboModule and legacy NativeModules callers get consistent callable methods instead of raw constants (avoiding "isJailBroken is not a function" crashes). It also adds an Expo SDK 55 example app and local packaging/build scripts to validate new-arch behavior end-to-end.

Changes:

  • Add CoreLocation framework linkage (podspec) and header import (ObjC++) for new-arch iOS builds.
  • Update JS entrypoint to detect/load the TurboModule via try/catch and wrap exported “constant” values into callable functions uniformly.
  • Introduce ExampleExpo55/ and supporting scripts/ignore rules for local tarball-based testing.

Reviewed changes

Copilot reviewed 11 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
package.json Adds bun-based pack/install/build scripts for the Expo 55 example workflow.
jail-monkey.podspec Links CoreLocation framework for iOS builds.
index.tsx Reworks TurboModule detection + wraps constants into callable functions across module sources.
JailMonkey/JailMonkey.mm Imports CoreLocation header needed by code paths using CLLocationManager.
ExampleExpo55/tsconfig.json Adds strict TS config for the Expo 55 example app.
ExampleExpo55/package.json Defines Expo 55 example dependencies and scripts using the packed tarball.
ExampleExpo55/index.tsx Registers a SafeArea-wrapped root component for the example app.
ExampleExpo55/App.tsx Example UI exercising the module APIs (new-arch oriented).
ExampleExpo55/app.json Expo app configuration and asset wiring.
ExampleExpo55/.gitignore Ignores generated native folders and typical Expo artifacts.
ExampleExpo55/assets/* Adds icon/splash assets for the Expo example app.
.npmignore Excludes ExampleExpo55 and local artifacts/ from npm package.
.gitignore Ignores local artifacts/ output and .vscode/settings.json.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread index.tsx Outdated
Comment on lines +4 to +30
@@ -6,9 +7,53 @@ const LINKING_ERROR =
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n';

// @ts-expect-error
const isTurboModuleEnabled = global.__turboModuleProxy != null;
// Prefer TurboModule when available.
// Expo SDK / RN setup can make `global.__turboModuleProxy` unreliable, so
// we attempt to load the TurboModule and fall back to legacy NativeModules.
let turboModule: any = null;
try {
// This will throw if the TurboModule isn't registered/available.
// eslint-disable-next-line @typescript-eslint/no-var-requires
turboModule = require('./specs/NativeJailMonkey.ts').default;
} catch {
turboModule = null;
}

export default isTurboModuleEnabled
? require('./specs/NativeJailMonkey.ts').default
: NativeModules.JailMonkey;
if (turboModule != null) {
// fall through to the `exported` constant below
}

const legacy = NativeModules.JailMonkey;
const wrapBool = (v: any) =>
typeof v === 'function' ? v : () => Boolean(v);

const source = turboModule ?? legacy;

@hbabathe hbabathe Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should show link error if suitable source is not found

Comment thread index.tsx Outdated
Comment on lines +54 to +57
rootedDetectionMethods:
typeof source?.rootedDetectionMethods === 'function'
? source.rootedDetectionMethods
: undefined,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread index.tsx Outdated
Comment thread index.tsx Outdated
Comment thread ExampleExpo55/App.tsx Outdated
@zaferatli

Copy link
Copy Markdown
Collaborator

@hbabathe can you check co-pilot comments, after we can merge

- Throw LINKING_ERROR via Proxy when neither TurboModule nor NativeModule
  resolves, so missing installs fail loudly instead of reporting a clean
  non-jailbroken device.
- Normalize rootedDetectionMethods to a callable on old-arch Android
  (constant object via getConstants) to match new-arch's function shape.
- Replace file-wide @ts-nocheck with narrow @ts-expect-error on the
  dynamic TurboModule require and the react-native peer-dep import.
- Drop the no-op `if (turboModule != null)` block.
- Remove contradictory `accessible={false} accessibilityLabel=""` from
  the ExampleExpo55 content View.
Switches ExampleProject to the artifacts tarball (matching ExampleExpo55)
and regenerates Podfile.lock so jail-monkey actually links on iOS.

NOTE: the legacy ExampleProject (RN 0.73 / Gradle 8.3 / AGP 8.1) still
needs a broader upgrade per PR review comments — it currently requires
JDK 17 (not the Android Studio JBR JDK 21) to avoid a JdkImageTransform
failure on core-for-system-modules.jar. Tracking a follow-up to bump
Gradle/AGP/RN here so JDK 21 builds work without an override.
@hbabathe

hbabathe commented Apr 20, 2026

Copy link
Copy Markdown
Contributor Author

Made updates suggested by the copilot.

Note:

  1. The scripts uses tgz refs for both example projects, this because bun package manager doesn't use symlink like yarn. This is just my preference.
  2. Legacy Example project is tested with different build and config. For my local(updated) setup I had to make few updates(RN bump and JDK and sdk changes) which are not part of this commit.

@levibuzolic

Copy link
Copy Markdown
Collaborator
  1. The scripts uses tgz refs for both example projects, this because bun package manager doesn't use symlink like yarn. This is just my preference.

Please use Yarn to align with the project, rather than adding new tooling.

@levibuzolic levibuzolic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, just a couple of minor cleanups, otherwise should be good to go.

Comment thread ExampleExpo/app.json

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reckon we'd like to rename this directory to ExampleExpo -- we'll likely want to update the SDK version here over time without creating a new exmaple project with every major SDK.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Comment thread ExampleExpo55/package.json Outdated
"expo-dev-client": "~55.0.19",
"expo-status-bar": "~55.0.4",
"expo-system-ui": "~55.0.11",
"jail-monkey": "file:../artifacts/jail-monkey-3.0.0.tgz",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would definitely prefer yarn symlinking here, so we can skip the build step + avoid hardcoding versions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok will do a smoke test with yarn and push changes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@zaferatli

Copy link
Copy Markdown
Collaborator

I’ll merge and release it tonight unless any issues are raised.

@zaferatli zaferatli merged commit 38298ac into GantMan:master Apr 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants