Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
85 changes: 60 additions & 25 deletions ios/ReactNativeBrownfield.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,18 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
* Default value: nil
*/
private var reactNativeFactory: RCTReactNativeFactory? = nil
/**
* Root view factory used to create React Native views.
*/
lazy private var rootViewFactory: RCTRootViewFactory? = {
return reactNativeFactory?.rootViewFactory
}()
private var hasStartedReactNative = false

private var factory: RCTReactNativeFactory {
if let existingFactory = reactNativeFactory {
return existingFactory
}

delegate.dependencyProvider = RCTAppDependencyProvider()
let createdFactory = RCTReactNativeFactory(delegate: delegate)
reactNativeFactory = createdFactory
return createdFactory
}

/**
* Starts React Native with default parameters.
Expand All @@ -88,7 +94,11 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
initialProps: [AnyHashable: Any]?,
launchOptions: [AnyHashable: Any]? = nil
) -> UIView? {
reactNativeFactory?.rootViewFactory.view(
let resolvedFactory = factory

let rootViewFactory = resolvedFactory.rootViewFactory

return rootViewFactory.view(
withModuleName: moduleName,
initialProperties: initialProps,
launchOptions: launchOptions
Expand All @@ -111,29 +121,54 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
* @param launchOptions Launch options, typically passed from AppDelegate.
*/
@objc public func startReactNative(onBundleLoaded: (() -> Void)?, launchOptions: [AnyHashable: Any]?) {
guard reactNativeFactory == nil else { return }

delegate.dependencyProvider = RCTAppDependencyProvider()
self.reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
guard !hasStartedReactNative else { return }
_ = launchOptions
_ = factory

if let onBundleLoaded {
self.onBundleLoaded = onBundleLoaded
if RCTIsNewArchEnabled() {
NotificationCenter.default.addObserver(
self,
selector: #selector(jsLoaded),
name: NSNotification.Name("RCTInstanceDidLoadBundle"),
object: nil
)
} else {
NotificationCenter.default.addObserver(
self,
selector: #selector(jsLoaded),
name: NSNotification.Name("RCTJavaScriptDidLoadNotification"),
object: nil
)
let notificationName: Notification.Name = RCTIsNewArchEnabled()
? Notification.Name("RCTInstanceDidLoadBundle")
: Notification.Name("RCTJavaScriptDidLoadNotification")

NotificationCenter.default.addObserver(
self,
selector: #selector(jsLoaded),
name: notificationName,
object: nil
)

if let bridge = reactNativeFactory?.bridge, !bridge.isLoading {
DispatchQueue.main.async { [weak self] in
self?.jsLoaded(Notification(name: notificationName))
}
}
}

hasStartedReactNative = true
}

/**
* 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 {
hasStartedReactNative = false
return
}

factory.bridge?.invalidate()

NotificationCenter.default.removeObserver(self)
onBundleLoaded = nil

hasStartedReactNative = false
reactNativeFactory = nil
}

@objc private func jsLoaded(_ notification: Notification) {
Expand Down