Skip to content

Commit 1919271

Browse files
feat: add method to deallocate reactNativeFactory instance
1 parent 5ac357b commit 1919271

8 files changed

Lines changed: 131 additions & 35 deletions

File tree

.changeset/olive-cloths-joke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@callstack/react-native-brownfield': minor
3+
---
4+
5+
feat: add method to deallocate reactNativeFactory instance

apps/AppleApp/Brownfield Apple App/components/ContentView.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ struct ContentView: View {
2727
.navigationBarHidden(true)
2828
.clipShape(RoundedRectangle(cornerRadius: 16))
2929
.background(Color(UIColor.systemBackground))
30+
31+
Button("Stop React Native") {
32+
ReactNativeBrownfield.shared.stopReactNative()
33+
}
34+
.buttonStyle(PlainButtonStyle())
35+
.padding(.top)
36+
.foregroundColor(.red)
3037
}
3138
.frame(maxWidth: .infinity, maxHeight: .infinity)
3239
.padding(16)

apps/RNApp/ios/Podfile.lock

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PODS:
22
- boost (1.84.0)
3-
- BrownfieldNavigation (3.0.0):
3+
- BrownfieldNavigation (3.5.1):
44
- boost
55
- DoubleConversion
66
- fast_float
@@ -28,7 +28,7 @@ PODS:
2828
- ReactCommon/turbomodule/core
2929
- SocketRocket
3030
- Yoga
31-
- Brownie (3.0.0):
31+
- Brownie (3.5.1):
3232
- boost
3333
- DoubleConversion
3434
- fast_float
@@ -1838,7 +1838,7 @@ PODS:
18381838
- React-RCTFBReactNativeSpec
18391839
- ReactCommon/turbomodule/core
18401840
- SocketRocket
1841-
- react-native-safe-area-context (5.6.2):
1841+
- react-native-safe-area-context (5.7.0):
18421842
- boost
18431843
- DoubleConversion
18441844
- fast_float
@@ -1856,8 +1856,8 @@ PODS:
18561856
- React-graphics
18571857
- React-ImageManager
18581858
- React-jsi
1859-
- react-native-safe-area-context/common (= 5.6.2)
1860-
- react-native-safe-area-context/fabric (= 5.6.2)
1859+
- react-native-safe-area-context/common (= 5.7.0)
1860+
- react-native-safe-area-context/fabric (= 5.7.0)
18611861
- React-NativeModulesApple
18621862
- React-RCTFabric
18631863
- React-renderercss
@@ -1868,7 +1868,7 @@ PODS:
18681868
- ReactCommon/turbomodule/core
18691869
- SocketRocket
18701870
- Yoga
1871-
- react-native-safe-area-context/common (5.6.2):
1871+
- react-native-safe-area-context/common (5.7.0):
18721872
- boost
18731873
- DoubleConversion
18741874
- fast_float
@@ -1896,7 +1896,7 @@ PODS:
18961896
- ReactCommon/turbomodule/core
18971897
- SocketRocket
18981898
- Yoga
1899-
- react-native-safe-area-context/fabric (5.6.2):
1899+
- react-native-safe-area-context/fabric (5.7.0):
19001900
- boost
19011901
- DoubleConversion
19021902
- fast_float
@@ -2378,7 +2378,7 @@ PODS:
23782378
- SocketRocket
23792379
- ReactAppDependencyProvider (0.82.1):
23802380
- ReactCodegen
2381-
- ReactBrownfield (3.0.0):
2381+
- ReactBrownfield (3.5.1):
23822382
- boost
23832383
- DoubleConversion
23842384
- fast_float
@@ -2494,7 +2494,7 @@ PODS:
24942494
- React-perflogger (= 0.82.1)
24952495
- React-utils (= 0.82.1)
24962496
- SocketRocket
2497-
- RNScreens (4.19.0):
2497+
- RNScreens (4.24.0):
24982498
- boost
24992499
- DoubleConversion
25002500
- fast_float
@@ -2521,10 +2521,10 @@ PODS:
25212521
- ReactCodegen
25222522
- ReactCommon/turbomodule/bridging
25232523
- ReactCommon/turbomodule/core
2524-
- RNScreens/common (= 4.19.0)
2524+
- RNScreens/common (= 4.24.0)
25252525
- SocketRocket
25262526
- Yoga
2527-
- RNScreens/common (4.19.0):
2527+
- RNScreens/common (4.24.0):
25282528
- boost
25292529
- DoubleConversion
25302530
- fast_float
@@ -2803,8 +2803,8 @@ EXTERNAL SOURCES:
28032803

28042804
SPEC CHECKSUMS:
28052805
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
2806-
BrownfieldNavigation: 12a34a451661d8f685beebab19d4ba7b43efc409
2807-
Brownie: 981350e32e072e5b55b624eb8810ba9bbc9683d9
2806+
BrownfieldNavigation: b25cd0c4b253b653743be92d141ffe475e42474d
2807+
Brownie: ac5a447e77a9d7713ebdb4e71a6083f00b4364f5
28082808
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
28092809
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
28102810
FBLazyVector: 0aa6183b9afe3c31fc65b5d1eeef1f3c19b63bfa
@@ -2844,7 +2844,7 @@ SPEC CHECKSUMS:
28442844
React-logger: 500f2fa5697d224e63c33d913c8a4765319e19bf
28452845
React-Mapbuffer: 4c50cf6af44286015a20a5995d5321f625c93459
28462846
React-microtasksnativemodule: a84b9331106616ab1fa36de9ae555718d4bbdcf5
2847-
react-native-safe-area-context: 0a3b034bb63a5b684dd2f5fffd3c90ef6ed41ee8
2847+
react-native-safe-area-context: eda63a662750758c1fdd7e719c9f1026c8d161cb
28482848
React-NativeModulesApple: efd0906463c79d9b86197dbcf0d58358dff8c5ed
28492849
React-oscompat: 95875e81f5d4b3c7b2c888d5bd2c9d83450d8bdb
28502850
React-perflogger: 2e229bf33e42c094fd64516d89ec1187a2b79b5b
@@ -2875,10 +2875,10 @@ SPEC CHECKSUMS:
28752875
React-utils: f06ff240e06e2bd4b34e48f1b34cac00866e8979
28762876
React-webperformancenativemodule: b3398f8175fa96d992c071b1fa59bd6f9646b840
28772877
ReactAppDependencyProvider: a45ef34bb22dc1c9b2ac1f74167d9a28af961176
2878-
ReactBrownfield: 03a2fd2f61109c00810b8d82af6f8907095191ed
2878+
ReactBrownfield: 9d232f72e023ff2cca3bd6d4de188b2292125c83
28792879
ReactCodegen: 0bce2d209e2e802589f4c5ff76d21618200e74cb
28802880
ReactCommon: 801eff8cb9c940c04d3a89ce399c343ee3eff654
2881-
RNScreens: d6413aeb1878cdafd3c721e2c5218faf5d5d3b13
2881+
RNScreens: e902eba58a27d3ad399a495d578e8aba3ea0f490
28822882
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
28832883
Yoga: 526f25666395d30c297d53154398ffd249eaf9e1
28842884

docs/docs/docs/api-reference/react-native-brownfield/objective-c.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ Starts React Native, produces an instance of React Native. You can use it to ini
5959
}];
6060
```
6161
62+
##### `stopReactNative`
63+
64+
Stops React Native and releases the underlying runtime. Safe to call multiple times. Call it after all React Native views are dismissed.
65+
66+
**Examples:**
67+
68+
```objc
69+
[[ReactNativeBrownfield shared] stopReactNative];
70+
```
71+
6272
##### `view`
6373

6474
Creates a React Native view for the specified module name.

docs/docs/docs/api-reference/react-native-brownfield/swift.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ ReactNativeBrownfield.shared.startReactNative(onBundleLoaded: {
5959
})
6060
```
6161

62+
##### `stopReactNative`
63+
64+
Stops React Native and releases the underlying runtime. Safe to call multiple times. Call it after all React Native views are dismissed.
65+
66+
**Examples:**
67+
68+
```swift
69+
ReactNativeBrownfield.shared.stopReactNative()
70+
```
71+
6272
##### `view`
6373

6474
Creates a React Native view for the specified module name.

packages/react-native-brownfield/ios/ExpoHostRuntime.swift

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ final class ExpoHostRuntime {
1414
private var reactNativeFactory: RCTReactNativeFactory?
1515
private var expoDelegate: ExpoAppDelegate?
1616

17+
private var factory: RCTReactNativeFactory {
18+
if let existingFactory = reactNativeFactory {
19+
return existingFactory
20+
}
21+
22+
delegate.dependencyProvider = RCTAppDependencyProvider()
23+
let createdFactory = ExpoReactNativeFactory(delegate: delegate)
24+
reactNativeFactory = createdFactory
25+
return createdFactory
26+
}
27+
1728
/**
1829
* Starts React Native with default parameters.
1930
*/
@@ -46,6 +57,24 @@ final class ExpoHostRuntime {
4657
}
4758
}
4859

60+
/**
61+
* Stops React Native and releases the underlying factory instance.
62+
*/
63+
public func stopReactNative() {
64+
if !Thread.isMainThread {
65+
DispatchQueue.main.async { [weak self] in self?.stopReactNative() }
66+
return
67+
}
68+
69+
#if !EXPO_SDK_GTE_55
70+
guard let factory = reactNativeFactory else { return }
71+
factory.bridge?.invalidate()
72+
#endif
73+
74+
reactNativeFactory = nil
75+
expoDelegate = nil
76+
}
77+
4978
/**
5079
* Path to JavaScript root.
5180
* Default value: ".expo/.virtual-metro-entry"
@@ -125,19 +154,19 @@ final class ExpoHostRuntime {
125154
// below: https://github.com/expo/expo/commit/2013760c46cde1404872d181a691da72fbf207a4
126155
// has moved the recreateRootView method to ExpoReactNativeFactory
127156
#if EXPO_SDK_GTE_55 // this define comes from the Brownfield Expo config plugin
128-
return (reactNativeFactory as? ExpoReactNativeFactory)?.recreateRootView(
129-
withBundleURL: bundleURL,
130-
moduleName: moduleName,
131-
initialProps: initialProps,
132-
launchOptions: launchOptions
133-
)
157+
return (factory as? ExpoReactNativeFactory)?.recreateRootView(
158+
withBundleURL: bundleURL,
159+
moduleName: moduleName,
160+
initialProps: initialProps,
161+
launchOptions: launchOptions
162+
)
134163
#else
135-
return expoDelegate?.recreateRootView(
136-
withBundleURL: bundleURL,
137-
moduleName: moduleName,
138-
initialProps: initialProps,
139-
launchOptions: launchOptions
140-
)
164+
return expoDelegate?.recreateRootView(
165+
withBundleURL: bundleURL,
166+
moduleName: moduleName,
167+
initialProps: initialProps,
168+
launchOptions: launchOptions
169+
)
141170
#endif
142171
}
143172
}

packages/react-native-brownfield/ios/ReactNativeBrownfield.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ internal import Expo
8282
#endif
8383
}
8484

85+
/**
86+
* Stops React Native.
87+
*/
88+
@objc public func stopReactNative() {
89+
#if canImport(Expo)
90+
ExpoHostRuntime.shared.stopReactNative()
91+
#else
92+
ReactNativeHostRuntime.shared.stopReactNative()
93+
#endif
94+
}
95+
8596
@objc public func view(
8697
moduleName: String,
8798
initialProps: [AnyHashable: Any]?,

packages/react-native-brownfield/ios/ReactNativeHostRuntime.swift

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ final class ReactNativeHostRuntime {
5959
delegate.bundlePath = bundlePath
6060
}
6161
}
62+
6263
/**
6364
* Bundle instance to lookup the JavaScript bundle.
6465
* Default value: Bundle.main
@@ -68,6 +69,7 @@ final class ReactNativeHostRuntime {
6869
delegate.bundle = bundle
6970
}
7071
}
72+
7173
/**
7274
* Dynamic bundle URL provider called on every bundle load.
7375
* When set, this overrides the default bundleURL() behavior in the delegate.
@@ -79,17 +81,23 @@ final class ReactNativeHostRuntime {
7981
delegate.bundleURLOverride = bundleURLOverride
8082
}
8183
}
84+
8285
/**
8386
* React Native factory instance created when starting React Native.
8487
* Default value: nil
8588
*/
8689
private var reactNativeFactory: RCTReactNativeFactory? = nil
87-
/**
88-
* Root view factory used to create React Native views.
89-
*/
90-
lazy private var rootViewFactory: RCTRootViewFactory? = {
91-
return reactNativeFactory?.rootViewFactory
92-
}()
90+
91+
private var factory: RCTReactNativeFactory {
92+
if let existingFactory = reactNativeFactory {
93+
return existingFactory
94+
}
95+
96+
delegate.dependencyProvider = RCTAppDependencyProvider()
97+
let createdFactory = RCTReactNativeFactory(delegate: delegate)
98+
reactNativeFactory = createdFactory
99+
return createdFactory
100+
}
93101

94102
/**
95103
* Starts React Native with default parameters.
@@ -98,12 +106,28 @@ final class ReactNativeHostRuntime {
98106
startReactNative(onBundleLoaded: nil)
99107
}
100108

109+
/**
110+
* Stops React Native and releases the underlying factory instance.
111+
*/
112+
public func stopReactNative() {
113+
if !Thread.isMainThread {
114+
DispatchQueue.main.async { [weak self] in self?.stopReactNative() }
115+
return
116+
}
117+
118+
guard let factory = reactNativeFactory else { return }
119+
120+
factory.bridge?.invalidate()
121+
122+
reactNativeFactory = nil
123+
}
124+
101125
public func view(
102126
moduleName: String,
103127
initialProps: [AnyHashable: Any]?,
104128
launchOptions: [AnyHashable: Any]? = nil
105129
) -> UIView? {
106-
rootViewFactory?.view(
130+
factory.rootViewFactory.view(
107131
withModuleName: moduleName,
108132
initialProperties: initialProps,
109133
launchOptions: launchOptions

0 commit comments

Comments
 (0)