Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/OBJECTIVE_C.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ Examples:
}, launchOptions];
```

`stopReactNative`

Stops React Native and releases the underlying runtime. Safe to call multiple times. Call it after all React Native views are dismissed.

Examples:

```objc
[[ReactNativeBrownfield shared] stopReactNative];
```

`view`

Creates a React Native view for the specified module name.
Expand Down
10 changes: 10 additions & 0 deletions docs/SWIFT.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ ReactNativeBrownfield.shared.startReactNative(onBundleLoaded: {
}, launchOptions: launchOptions)
```

`stopReactNative`

Stops React Native and releases the underlying runtime. Safe to call multiple times. Call it after all React Native views are dismissed.

Examples:

```swift
ReactNativeBrownfield.shared.stopReactNative()
```

`view`

Creates a React Native view for the specified module name.
Expand Down
8 changes: 8 additions & 0 deletions example/swift/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ struct ContentView: View {
.font(.title)
.bold()
.padding()
.multilineTextAlignment(.center)

NavigationLink("Push React Native Screen") {
ReactNativeView(moduleName: "ReactNative")
.navigationBarHidden(true)
}

Button("Stop React Native") {
ReactNativeBrownfield.shared.stopReactNative()
}
.buttonStyle(PlainButtonStyle())
.padding(.top)
.foregroundColor(.red)
}
}
}
Expand Down
34 changes: 30 additions & 4 deletions ios/ReactNativeBrownfield.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
private var onBundleLoaded: (() -> Void)?
private var delegate = ReactNativeBrownfieldDelegate()

private func checkFactoryInitialized(launchOptions: [AnyHashable: Any]? = nil) {
if reactNativeFactory == nil {
delegate.dependencyProvider = RCTAppDependencyProvider()
self.reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Can we create w getter on the factory that initizalizes it if it's nil? It should remove the need to call this function


/**
* Path to JavaScript root.
* Default value: "index"
Expand Down Expand Up @@ -88,7 +95,9 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
initialProps: [AnyHashable: Any]?,
launchOptions: [AnyHashable: Any]? = nil
) -> UIView? {
reactNativeFactory?.rootViewFactory.view(
checkFactoryInitialized(launchOptions: launchOptions)

return reactNativeFactory?.rootViewFactory.view(
withModuleName: moduleName,
initialProperties: initialProps,
launchOptions: launchOptions
Expand All @@ -112,9 +121,7 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
*/
@objc public func startReactNative(onBundleLoaded: (() -> Void)?, launchOptions: [AnyHashable: Any]?) {
guard reactNativeFactory == nil else { return }

delegate.dependencyProvider = RCTAppDependencyProvider()
self.reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
checkFactoryInitialized(launchOptions: launchOptions)

if let onBundleLoaded {
self.onBundleLoaded = onBundleLoaded
Expand All @@ -136,6 +143,25 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
}
}

/**
* Stops React Native and releases the underlying factory instance.
*/
@objc public func stopReactNative() {
if !Thread.isMainThread {
DispatchQueue.main.async { [weak self] in self?.stopReactNative() }
return
}

guard let factory = reactNativeFactory else { return }
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
guard let factory = reactNativeFactory else { return }
guard let reactNativeFactory else { return }


factory.bridge?.invalidate()

NotificationCenter.default.removeObserver(self)
onBundleLoaded = nil

reactNativeFactory = nil
Comment on lines 165 to 171
Copy link
Member

Choose a reason for hiding this comment

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

Did you test the scenario of what happens when react native view is still shown while calling this? Let's make sure it doesn't crash the app.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe there is a way to detect current react native surfaces in the app and show a warning if the user tries to do this with views still on screen

}

@objc private func jsLoaded(_ notification: Notification) {
onBundleLoaded?()
onBundleLoaded = nil
Expand Down
Loading