Skip to content

Commit 594e60a

Browse files
committed
feat: add JSBundle load for Expo and make it reusable
1 parent d4420f4 commit 594e60a

5 files changed

Lines changed: 52 additions & 97 deletions

File tree

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal import Expo
99
final class ExpoHostRuntime {
1010
static let shared = ExpoHostRuntime()
1111

12+
private let jsBundleLoadObserver = JSBundleLoadObserver()
1213
private var delegate = ExpoHostRuntimeDelegate()
1314
private var reactNativeFactory: RCTReactNativeFactory?
1415
private var expoDelegate: ExpoAppDelegate?
@@ -35,6 +36,10 @@ final class ExpoHostRuntime {
3536
let appDelegate = ExpoAppDelegate()
3637
appDelegate.bindReactNativeFactory(factory)
3738
expoDelegate = appDelegate
39+
40+
if let onBundleLoaded {
41+
jsBundleLoadObserver.observeOnce(onBundleLoaded: onBundleLoaded)
42+
}
3843
}
3944

4045
/**
@@ -91,19 +96,15 @@ final class ExpoHostRuntime {
9196
moduleName: String,
9297
initialProps: [AnyHashable: Any]?,
9398
launchOptions: [AnyHashable: Any]?
94-
) -> UIView {
99+
) -> UIView? {
95100
let bundleURL = delegate.bundleURL()
96101

97-
if let view = expoDelegate?.recreateRootView(
102+
return expoDelegate?.recreateRootView(
98103
withBundleURL: bundleURL,
99104
moduleName: moduleName,
100105
initialProps: initialProps,
101106
launchOptions: launchOptions
102-
) {
103-
return view
104-
}
105-
106-
return UIView(frame: .zero)
107+
)
107108
}
108109
}
109110

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Foundation
2+
internal import React
3+
4+
final class JSBundleLoadObserver {
5+
private var onBundleLoaded: (() -> Void)?
6+
private var observerToken: NSObjectProtocol?
7+
8+
func observeOnce(onBundleLoaded: @escaping () -> Void) {
9+
removeObserverIfNeeded()
10+
self.onBundleLoaded = onBundleLoaded
11+
12+
observerToken = NotificationCenter.default.addObserver(
13+
forName: NSNotification.Name("RCTInstanceDidLoadBundle"),
14+
object: nil,
15+
queue: nil
16+
) { [weak self] _ in
17+
self?.notifyAndClear()
18+
}
19+
}
20+
21+
deinit {
22+
removeObserverIfNeeded()
23+
}
24+
25+
private func notifyAndClear() {
26+
let callback = onBundleLoaded
27+
onBundleLoaded = nil
28+
removeObserverIfNeeded()
29+
callback?()
30+
}
31+
32+
private func removeObserverIfNeeded() {
33+
if let observerToken {
34+
NotificationCenter.default.removeObserver(observerToken)
35+
self.observerToken = nil
36+
}
37+
}
38+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal import Expo
99

1010
/**
1111
* Path to JavaScript root.
12-
* Default value: "index"
12+
* Default value: "index" for bare React Native, ".expo/.virtual-metro-entry" when built with Expo.
1313
*/
1414
@objc public var entryFile: String = {
1515
#if canImport(Expo)
@@ -61,7 +61,7 @@ internal import Expo
6161
* Returns a URL to load a custom bundle, or nil to use default behavior.
6262
* Default value: nil
6363
*/
64-
@objc public var bundleURLOverride: (() -> URL?)? {
64+
@objc public var bundleURLOverride: (() -> URL?)? = nil {
6565
didSet {
6666
#if canImport(Expo)
6767
ExpoHostRuntime.shared.bundleURLOverride = bundleURLOverride
@@ -103,7 +103,7 @@ internal import Expo
103103
}
104104

105105
/**
106-
* Mirrors host manager app delegate API for bare React Native.
106+
* Mirrors the host runtime app delegate API, forwarding to Expo or bare React Native as appropriate.
107107
*/
108108
@objc public func application(
109109
_ application: UIApplication,

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
3131
}
3232
}
3333

34-
final class ReactNativeHostRuntime: NSObject {
34+
final class ReactNativeHostRuntime {
3535
public static let shared = ReactNativeHostRuntime()
36-
private var onBundleLoaded: (() -> Void)?
36+
private let jsBundleLoadObserver = JSBundleLoadObserver()
3737
private var delegate = ReactNativeBrownfieldDelegate()
3838

3939
/**
@@ -128,28 +128,7 @@ final class ReactNativeHostRuntime: NSObject {
128128
self.reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
129129

130130
if let onBundleLoaded {
131-
self.onBundleLoaded = onBundleLoaded
132-
if RCTIsNewArchEnabled() {
133-
NotificationCenter.default.addObserver(
134-
self,
135-
selector: #selector(jsLoaded),
136-
name: NSNotification.Name("RCTInstanceDidLoadBundle"),
137-
object: nil
138-
)
139-
} else {
140-
NotificationCenter.default.addObserver(
141-
self,
142-
selector: #selector(jsLoaded),
143-
name: NSNotification.Name("RCTJavaScriptDidLoadNotification"),
144-
object: nil
145-
)
146-
}
131+
jsBundleLoadObserver.observeOnce(onBundleLoaded: onBundleLoaded)
147132
}
148133
}
149-
150-
@objc private func jsLoaded(_ notification: Notification) {
151-
onBundleLoaded?()
152-
onBundleLoaded = nil
153-
NotificationCenter.default.removeObserver(self)
154-
}
155134
}

packages/react-native-brownfield/src/expo-config-plugin/template/ios/ReactNativeHostManager.swift

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)