-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ios][core] Create JSI host object to access the new expo modules #15847
Conversation
block:(nonnull JSAsyncFunctionBlock)block | ||
{ | ||
jsi::Function function = [_runtime createAsyncFunction:name argsCount:argsCount block:block]; | ||
_jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], function); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shoulda check if runtime is not null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shoulda check if runtime is not null
Do you? You marked runtime as nonnull
able above in the same file 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's nonnull
only when it's passed as an argument to the initializer of JavaScriptObject
. _runtime
is marked as a __weak
property, so it might be deallocated before the object gets deallocated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks good, even though I'm not accustomed to reading the mix of ObjC(++)/Swift code tangled together in one PR 😅
I've got one question - can you be even more explicit in regards to nullability in ObjC(++). You've annotated majority of the code, but there're some gaps (especially in functions params definitions).
block:(nonnull JSAsyncFunctionBlock)block | ||
{ | ||
jsi::Function function = [_runtime createAsyncFunction:name argsCount:argsCount block:block]; | ||
_jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], function); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shoulda check if runtime is not null
Do you? You marked runtime as nonnull
able above in the same file 🤔
ExpoModulesHostObject::ExpoModulesHostObject(SwiftInteropBridge *swiftInterop) : swiftInterop(swiftInterop) {} | ||
|
||
ExpoModulesHostObject::~ExpoModulesHostObject() { | ||
[swiftInterop setRuntime:nil]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it intentionally to setRuntime:nil
here? what if someone do global.ExpoModules = null;
in javascript and after GC, will it also call to here and cleanup the runtime?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's intentional. Our JavaScriptRuntime
wrapper holds the reference to jsi::Runtime
and shared pointer to react::CallInvoker
and so we should make sure they both can deallocate when the JS engine wants it.
Well, if you do global.ExpoModules = null
then you actually opt into some unexpected behavior 🤪
We can also prevent this case with such JS code:
const ExpoModules = global.ExpoModules;
delete global.ExpoModules;
// And now use `ExpoModules` instead of `global.ExpoModules`.
This shouldn't deallocate the object 🤔 I can check out how that works in #15848
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀
43121bf
to
2c15564
Compare
Bump `expo-modules-core` to `0.7.0`: # Changelog: ## 0.7.0 — 2022-01-26 ### 🎉 New features - Allow accessing `RCTBridge` from the modules on iOS. ([#15816](expo/expo#15816) by [@tsapeta](https://github.com/tsapeta)) - Added support for native callbacks through the view props in Sweet API on iOS. ([#15731](expo/expo#15731) by [@tsapeta](https://github.com/tsapeta)) - Added support for native callbacks through the view props in Sweet API on Android. ([#15743](expo/expo#15743) by [@lukmccall](https://github.com/lukmccall)) - The `ModuleDefinition` will use class name if the `name` component wasn't provided in Sweet API on Android. ([#15738](expo/expo#15738) by [@lukmccall](https://github.com/lukmccall)) - Added `onViewDestroys` component to the `ViewManager` in Sweet API on Android. ([#15740](expo/expo#15740) by [@lukmccall](https://github.com/lukmccall)) - Added shortened `constants` component that takes `vargs Pair<String, Any?>` as an argument in Sweet API on Android. ([#15742](expo/expo#15742) by [@lukmccall](https://github.com/lukmccall)) - Introduced the concept of chainable exceptions in Sweet API on iOS. ([#15813](expo/expo#15813) by [@tsapeta](https://github.com/tsapeta)) - Sweet function closures can throw errors on iOS. ([#15849](expo/expo#15849) by [@tsapeta](https://github.com/tsapeta)) - Add `requireNativeModule` function to replace accessing native modules from `NativeModulesProxy`. ([#15848](expo/expo#15848) by [@tsapeta](https://github.com/tsapeta)) - Implemented basic functionality of JSI host object to replace `NativeModulesProxy` on iOS. ([#15847](expo/expo#15847) by [@tsapeta](https://github.com/tsapeta)) ### 🐛 Bug fixes - It's no longer possible to directly call methods from the `ModuleDefinition` in the `ViewManagers` on Android. ([#15741](expo/expo#15741) by [@lukmccall](https://github.com/lukmccall)) - Fix compatibility with react-native 0.66. ([#15914](expo/expo#15914) by [@Kudo](https://github.com/kudo)) ## 0.6.4 — 2022-01-05 ### 🐛 Bug fixes - Fix `ReactInstanceManager.onHostPause` exception from moving Android apps to background. ([#15748](expo/expo#15748) by [@Kudo](https://github.com/kudo)) ## 0.6.3 — 2021-12-16 ### 🐛 Bug fixes - Fixed the deep link wasn't passed to the application if the application wasn't running when the deep link was sent. ([#15593](expo/expo#15593) by [@lukmccall](https://github.com/lukmccall)) ## 0.6.2 — 2021-12-15 ### 🎉 New features - Add `onNewIntent` and `onBackPressed` support to `ReactActivityLifecycleListener`. ([#15550](expo/expo#15550) by [@Kudo](https://github.com/Kudo))
Why
Step further to integrate our new modules architecture with JSI and running native functions synchronously.
How
ExpoModulesHostObject
that installs into the runtime asglobal.ExpoModules
. The host object is an abstract object where we can define our own property getter, allowing us to create JS object for each module lazily.JavaScriptRuntime
) and objects (JavaScriptObject
) in ObjC++ to expose some functionalities to Swift.isAsync
property to sweet functions andrunSynchronously()
modifier.AppContext
can now access the runtime, so the modules can access the runtime too!Further plans
expo-random
to be an expo module and be the first to use synchronous functions.useJavaScriptRuntime(true)
definition component?ExpoModulesProxySpec
TurboModule which was an alternative implementation toNativeModulesProxy
— the solution with host object is much better, more scalable and that lets us do much more magic in the future, so we can drop the TurboModule (it wasn't used by default anyway).JavaScriptRuntime
andJavaScriptObject
are thread-safe (another addition to bare JSI objects).ModuleHolder
to the respective functions. See my comment inJavaScriptUtils.swift
requireNativeModule(moduleName)
instead ofNativeModulesProxy
(see related PR [core] Add requireNativeModule function #15848). We also need to replace JS examples in the docs to use this new function because we don't want to introduce developers into the proxy concept.Test Plan
expo-haptics
with a new synchronous function as a playground, all worked as expected.