@callstack/react-native-brownfield 3.9.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/ReactBrownfield.podspec +23 -3
  3. package/ios/BrownfieldDevLoadingViewBridge.h +11 -0
  4. package/ios/BrownfieldDevLoadingViewBridge.m +12 -0
  5. package/ios/{ExpoHostRuntime.swift → Expo/ExpoHostRuntime.swift} +65 -23
  6. package/ios/ReactNativeBrownfield.swift +14 -0
  7. package/ios/ReactNativeBrownfield.xcodeproj/project.pbxproj +74 -24
  8. package/ios/{ReactNativeHostRuntime.swift → Vanilla/ReactNativeHostRuntime.swift} +40 -12
  9. package/ios/swiftpm/Package.swift +27 -0
  10. package/ios/swiftpm/Sources/BrownfieldBundleSupport/BrownfieldBundleURLResolver.swift +28 -0
  11. package/ios/swiftpm/Tests/BrownfieldBundleSupportTests/BrownfieldBundleURLResolverTests.swift +156 -0
  12. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
  13. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
  14. package/lib/commonjs/expo-config-plugin/ios/withFmtFix.js +12 -0
  15. package/lib/commonjs/expo-config-plugin/ios/withFmtFix.js.map +1 -0
  16. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
  17. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
  18. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +6 -1
  19. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  20. package/lib/commonjs/expo-config-plugin/logging.js +1 -1
  21. package/lib/commonjs/expo-config-plugin/logging.js.map +1 -1
  22. package/lib/commonjs/expo-config-plugin/template/ios/FrameworkInterface.swift +4 -1
  23. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
  24. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
  25. package/lib/module/expo-config-plugin/ios/withFmtFix.js +12 -0
  26. package/lib/module/expo-config-plugin/ios/withFmtFix.js.map +1 -0
  27. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
  28. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
  29. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +6 -1
  30. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  31. package/lib/module/expo-config-plugin/logging.js +1 -1
  32. package/lib/module/expo-config-plugin/logging.js.map +1 -1
  33. package/lib/module/expo-config-plugin/template/ios/FrameworkInterface.swift +4 -1
  34. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withFmtFix.d.ts +5 -0
  36. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withFmtFix.d.ts.map +1 -0
  37. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts +14 -0
  39. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/src/expo-config-plugin/logging.d.ts +1 -0
  41. package/lib/typescript/commonjs/src/expo-config-plugin/logging.d.ts.map +1 -1
  42. package/lib/typescript/module/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
  43. package/lib/typescript/module/src/expo-config-plugin/ios/withFmtFix.d.ts +5 -0
  44. package/lib/typescript/module/src/expo-config-plugin/ios/withFmtFix.d.ts.map +1 -0
  45. package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
  46. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts +14 -0
  47. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  48. package/lib/typescript/module/src/expo-config-plugin/logging.d.ts +1 -0
  49. package/lib/typescript/module/src/expo-config-plugin/logging.d.ts.map +1 -1
  50. package/package.json +3 -3
  51. package/src/expo-config-plugin/ios/withBrownfieldIos.ts +14 -2
  52. package/src/expo-config-plugin/ios/withFmtFix.ts +72 -0
  53. package/src/expo-config-plugin/ios/withIosFrameworkFiles.ts +10 -10
  54. package/src/expo-config-plugin/ios/xcodeHelpers.ts +134 -14
  55. package/src/expo-config-plugin/logging.ts +4 -0
  56. package/src/expo-config-plugin/template/ios/FrameworkInterface.swift +4 -1
  57. /package/ios/{ReactNativeView.swift → Views/ReactNativeView.swift} +0 -0
  58. /package/ios/{ReactNativeViewController.swift → Views/ReactNativeViewController.swift} +0 -0
  59. /package/ios/{BrownfieldBundlePathResolver.swift → swiftpm/Sources/BrownfieldBundleSupport/BrownfieldBundlePathResolver.swift} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @callstack/react-native-brownfield
2
2
 
3
+ ## 3.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#317](https://github.com/callstack/react-native-brownfield/pull/317) [`c04301b`](https://github.com/callstack/react-native-brownfield/commit/c04301b79d288ab108aaa44d7c79dd876b8405e9) Thanks [@adamTrz](https://github.com/adamTrz)! - Add an opt-in iOS Debug mode for loading the embedded JavaScript bundle with `preferEmbeddedBundleInDebug`, fix `bundleURLOverride` fallback behavior when the override returns `nil`, and add native bundle-resolution tests.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#361](https://github.com/callstack/react-native-brownfield/pull/361) [`aa14eb2`](https://github.com/callstack/react-native-brownfield/commit/aa14eb26e26ea1e6075b91027237e4b41b932b57) Thanks [@hurali97](https://github.com/hurali97)! - make EXUpdates as a dependency only when installed
12
+
13
+ - [#347](https://github.com/callstack/react-native-brownfield/pull/347) [`c19a81b`](https://github.com/callstack/react-native-brownfield/commit/c19a81b7502cc458d742ab947186591bb69cb261) Thanks [@marcinszalski-callstack](https://github.com/marcinszalski-callstack)! - fix: fix method to deallocate reactNativeFactory instance for expo
14
+
15
+ - Updated dependencies [[`3715ac7`](https://github.com/callstack/react-native-brownfield/commit/3715ac7783000756ef1c66ecfe24a85c5e43abab), [`05c557d`](https://github.com/callstack/react-native-brownfield/commit/05c557da9e2b6fca02e6e1a0b0fe71a909bab15f)]:
16
+ - @callstack/brownfield-cli@3.11.0
17
+
18
+ ## 3.10.0
19
+
20
+ ### Patch Changes
21
+
22
+ - [#323](https://github.com/callstack/react-native-brownfield/pull/323) [`3456d3a`](https://github.com/callstack/react-native-brownfield/commit/3456d3aded18002475def4b79889979e76e1db5e) Thanks [@artus9033](https://github.com/artus9033)! - Force `BUILD_LIBRARY_FOR_DISTRIBUTION` / `SWIFT_EMIT_MODULE_INTERFACE` on the CocoaPods ReactBrownfield, Brownie and BrownfieldNavigation targets so Release builds emit `.swiftinterface` files and `xcodebuild -create-xcframework` (brownfield `package:ios`) can merge slices.
23
+
24
+ - Updated dependencies [[`3456d3a`](https://github.com/callstack/react-native-brownfield/commit/3456d3aded18002475def4b79889979e76e1db5e)]:
25
+ - @callstack/brownfield-cli@3.10.0
26
+
3
27
  ## 3.9.0
4
28
 
5
29
  ### Minor Changes
@@ -2,6 +2,18 @@ require 'json'
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
4
 
5
+ expo_updates_installed = lambda do
6
+ Pod::Executable.execute_command('node', [
7
+ '-p',
8
+ 'require.resolve("expo-updates/package.json", { paths: [process.argv[1]] })',
9
+ Pod::Config.instance.installation_root.to_s,
10
+ ])
11
+
12
+ true
13
+ rescue
14
+ false
15
+ end
16
+
5
17
  Pod::Spec.new do |spec|
6
18
  spec.name = "ReactBrownfield"
7
19
  spec.version = package['version']
@@ -15,17 +27,25 @@ Pod::Spec.new do |spec|
15
27
  spec.module_name = "ReactBrownfield"
16
28
  spec.source = { :git => "git@github.com:callstack/react-native-brownfield.git", :tag => "#{spec.version}" }
17
29
  spec.source_files = "ios/**/*.{h,m,mm,swift}"
30
+ spec.exclude_files = "ios/swiftpm/Package.swift", "ios/swiftpm/Tests/**/*"
18
31
  spec.pod_target_xcconfig = {
32
+ # below: needed to build the XCFramework with `.swiftinterface` files, required by xcodebuild -create-xcframework to succeed
19
33
  'DEFINES_MODULE' => 'YES',
34
+ 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES',
35
+ 'SWIFT_EMIT_MODULE_INTERFACE' => 'YES',
20
36
  'OTHER_SWIFT_FLAGS' => "-enable-experimental-feature AccessLevelOnImport"
21
37
  }
22
38
 
39
+ if ENV['RCT_USE_PREBUILT_RNCORE'] == '1'
40
+ spec.dependency 'React-Core-prebuilt'
41
+ end
42
+
23
43
  spec.dependency 'ReactAppDependencyProvider'
24
- add_dependency(spec, "React-RCTAppDelegate")
25
-
44
+ spec.dependency 'React-RCTAppDelegate'
45
+
26
46
  if ENV['REACT_NATIVE_BROWNFIELD_USE_EXPO_HOST'] == '1'
27
47
  spec.dependency 'Expo'
28
- spec.dependency 'EXUpdates'
48
+ spec.dependency 'EXUpdates' if expo_updates_installed.call
29
49
  end
30
50
 
31
51
  install_modules_dependencies(spec)
@@ -0,0 +1,11 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface BrownfieldDevLoadingViewBridge : NSObject
6
+
7
+ + (void)setEnabled:(BOOL)enabled;
8
+
9
+ @end
10
+
11
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,12 @@
1
+ #import "BrownfieldDevLoadingViewBridge.h"
2
+
3
+ #import <React/RCTDevLoadingViewSetEnabled.h>
4
+
5
+ @implementation BrownfieldDevLoadingViewBridge
6
+
7
+ + (void)setEnabled:(BOOL)enabled
8
+ {
9
+ RCTDevLoadingViewSetEnabled(enabled);
10
+ }
11
+
12
+ @end
@@ -1,9 +1,8 @@
1
+ #if canImport(Expo)
1
2
  import UIKit
2
3
  internal import React
3
- internal import React_RCTAppDelegate
4
4
  internal import ReactAppDependencyProvider
5
5
 
6
- #if canImport(Expo)
7
6
  internal import Expo
8
7
  #if canImport(EXUpdates)
9
8
  internal import EXUpdates
@@ -17,6 +16,16 @@ final class ExpoHostRuntime {
17
16
  private var reactNativeFactory: RCTReactNativeFactory?
18
17
  private var expoDelegate: ExpoAppDelegate?
19
18
 
19
+ private func configureDevLoadingView(with bundleURL: URL? = nil) {
20
+ #if DEBUG
21
+ let resolvedBundleURL = bundleURL ?? delegate.bundleURL()
22
+ let shouldDisableDevLoadingView =
23
+ preferEmbeddedBundleInDebug && (resolvedBundleURL?.isFileURL ?? false)
24
+
25
+ BrownfieldDevLoadingViewBridge.setEnabled(!shouldDisableDevLoadingView)
26
+ #endif
27
+ }
28
+
20
29
  /**
21
30
  * Starts React Native with default parameters.
22
31
  */
@@ -36,12 +45,12 @@ final class ExpoHostRuntime {
36
45
  reactNativeFactory = ExpoReactNativeFactory(delegate: delegate)
37
46
  // below: https://github.com/expo/expo/pull/39418/changes/5abd332b55b2ee7daee848284ed5f7fe1639452e
38
47
  // has removed bindReactNativeFactory method from ExpoAppDelegate
39
- #if !EXPO_SDK_GTE_55 // this define comes from the Brownfield Expo config plugin
48
+ #if !EXPO_SDK_GTE_55 // this define comes from the Brownfield Expo config plugin
40
49
  guard let reactNativeFactory else { return }
41
50
  appDelegate.bindReactNativeFactory(reactNativeFactory)
42
51
  #endif
43
52
  expoDelegate = appDelegate
44
-
53
+
45
54
  if let onBundleLoaded {
46
55
  jsBundleLoadObserver.observeOnce(onBundleLoaded: onBundleLoaded)
47
56
  }
@@ -56,6 +65,9 @@ final class ExpoHostRuntime {
56
65
  return
57
66
  }
58
67
 
68
+ if let rootViewFactory = reactNativeFactory?.rootViewFactory {
69
+ (rootViewFactory as AnyObject).setValue(nil, forKey: "reactHost")
70
+ }
59
71
  reactNativeFactory = nil
60
72
  expoDelegate = nil
61
73
  }
@@ -88,6 +100,17 @@ final class ExpoHostRuntime {
88
100
  delegate.bundle = bundle
89
101
  }
90
102
  }
103
+
104
+ /**
105
+ * Prefer the embedded JavaScript bundle instead of Metro when this framework is built in Debug.
106
+ * Default value: false
107
+ */
108
+ public var preferEmbeddedBundleInDebug: Bool = false {
109
+ didSet {
110
+ delegate.preferEmbeddedBundleInDebug = preferEmbeddedBundleInDebug
111
+ }
112
+ }
113
+
91
114
  /**
92
115
  * Dynamic bundle URL provider called on every bundle load.
93
116
  * When set, this overrides the default bundleURL() behavior in the delegate.
@@ -104,11 +127,11 @@ final class ExpoHostRuntime {
104
127
  _ application: UIApplication,
105
128
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
106
129
  ) -> Bool {
107
- #if canImport(EXUpdates)
130
+ #if canImport(EXUpdates)
108
131
  if !AppController.isInitialized() {
109
132
  AppController.initializeWithoutStarting()
110
133
  }
111
- #endif
134
+ #endif
112
135
  return ExpoAppDelegateSubscriberManager.application(application, didFinishLaunchingWithOptions: launchOptions)
113
136
  }
114
137
 
@@ -130,7 +153,7 @@ final class ExpoHostRuntime {
130
153
  let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
131
154
  return (expoDelegate?.application(application, continue: userActivity, restorationHandler: restorationHandler) ?? false) || result
132
155
  }
133
-
156
+
134
157
  func application(
135
158
  _ application: UIApplication,
136
159
  willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
@@ -144,10 +167,11 @@ final class ExpoHostRuntime {
144
167
  launchOptions: [AnyHashable: Any]?
145
168
  ) -> UIView? {
146
169
  let bundleURL = delegate.bundleURL()
170
+ configureDevLoadingView(with: bundleURL)
147
171
 
148
172
  // below: https://github.com/expo/expo/commit/2013760c46cde1404872d181a691da72fbf207a4
149
173
  // has moved the recreateRootView method to ExpoReactNativeFactory
150
- #if EXPO_SDK_GTE_55 // this define comes from the Brownfield Expo config plugin
174
+ #if EXPO_SDK_GTE_55 // this define comes from the Brownfield Expo config plugin
151
175
  return (reactNativeFactory as? ExpoReactNativeFactory)?.recreateRootView(
152
176
  withBundleURL: bundleURL,
153
177
  moduleName: moduleName,
@@ -169,6 +193,7 @@ class ExpoHostRuntimeDelegate: ExpoReactNativeFactoryDelegate {
169
193
  var entryFile = ".expo/.virtual-metro-entry"
170
194
  var bundlePath = "main.jsbundle"
171
195
  var bundle = Bundle.main
196
+ var preferEmbeddedBundleInDebug = false
172
197
  var bundleURLOverride: (() -> URL?)? = nil
173
198
 
174
199
  override func sourceURL(for bridge: RCTBridge) -> URL? {
@@ -177,27 +202,44 @@ class ExpoHostRuntimeDelegate: ExpoReactNativeFactoryDelegate {
177
202
  }
178
203
 
179
204
  override func bundleURL() -> URL? {
180
- if let bundleURLProvider = bundleURLOverride { return bundleURLProvider() }
181
- #if DEBUG
182
- return RCTBundleURLProvider.sharedSettings().jsBundleURL(
183
- forBundleRoot: entryFile)
184
- #else
185
- #if canImport(EXUpdates)
186
- if AppController.isInitialized(),
187
- let launchAssetURL = AppController.sharedInstance.launchAssetUrl() {
188
- return launchAssetURL
189
- }
190
- #endif
191
205
  do {
192
- let (resourceName, fileExtension) = try BrownfieldBundlePathResolver.resourceComponents(
193
- from: bundlePath
206
+ #if DEBUG
207
+ let isDebug = true
208
+ #else
209
+ let isDebug = false
210
+ #endif
211
+
212
+ if let overriddenURL = bundleURLOverride?() {
213
+ return overriddenURL
214
+ }
215
+
216
+ #if canImport(EXUpdates)
217
+ if !isDebug,
218
+ AppController.isInitialized(),
219
+ let launchAssetURL = AppController.sharedInstance.launchAssetUrl()
220
+ {
221
+ return launchAssetURL
222
+ }
223
+ #endif
224
+
225
+ // The override is resolved here so it wins over the Expo Updates launch asset
226
+ // and the closure is not evaluated a second time inside the shared resolver.
227
+ return try BrownfieldBundleURLResolver.resolve(
228
+ isDebug: isDebug,
229
+ preferEmbeddedBundleInDebug: preferEmbeddedBundleInDebug,
230
+ bundlePath: bundlePath,
231
+ bundle: bundle,
232
+ bundleURLOverride: nil,
233
+ metroURL: {
234
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(
235
+ forBundleRoot: entryFile
236
+ )
237
+ }
194
238
  )
195
- return bundle.url(forResource: resourceName, withExtension: fileExtension)
196
239
  } catch {
197
240
  assertionFailure("Invalid bundlePath '\(bundlePath)': \(error)")
198
241
  return nil
199
242
  }
200
- #endif
201
243
  }
202
244
  }
203
245
  #endif
@@ -55,6 +55,20 @@ internal import Expo
55
55
  }
56
56
  }
57
57
 
58
+ /**
59
+ * Prefer the embedded JavaScript bundle instead of Metro when this framework is built in Debug.
60
+ * Default value: false
61
+ */
62
+ @objc public var preferEmbeddedBundleInDebug: Bool = false {
63
+ didSet {
64
+ #if canImport(Expo)
65
+ ExpoHostRuntime.shared.preferEmbeddedBundleInDebug = preferEmbeddedBundleInDebug
66
+ #else
67
+ ReactNativeHostRuntime.shared.preferEmbeddedBundleInDebug = preferEmbeddedBundleInDebug
68
+ #endif
69
+ }
70
+ }
71
+
58
72
  /**
59
73
  * Dynamic bundle URL provider called on every bundle load.
60
74
  * When set, this overrides the default bundleURL() behavior in the delegate.
@@ -7,10 +7,16 @@
7
7
  objects = {
8
8
 
9
9
  /* Begin PBXBuildFile section */
10
- 696CB249230EF9EA000207AF /* ReactNativeBrownfieldNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 696CB243230EF9EA000207AF /* ReactNativeBrownfieldNotifications.m */; };
11
- 696CB24A230EF9EA000207AF /* ReactNativeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 696CB244230EF9EA000207AF /* ReactNativeViewController.m */; };
12
- 696CB24B230EF9EA000207AF /* ReactNativeBrownfield.m in Sources */ = {isa = PBXBuildFile; fileRef = 696CB245230EF9EA000207AF /* ReactNativeBrownfield.m */; };
13
- 696CB24C230EF9EA000207AF /* ReactNativeBrownfieldModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 696CB246230EF9EA000207AF /* ReactNativeBrownfieldModule.m */; };
10
+ 79FEE7742FB499C700E407DE /* BrownfieldBundlePathResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE76D2FB499C700E407DE /* BrownfieldBundlePathResolver.swift */; };
11
+ 79FEE7752FB499C700E407DE /* ReactNativeBrownfieldModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE7722FB499C700E407DE /* ReactNativeBrownfieldModule.mm */; };
12
+ 79FEE7762FB499C700E407DE /* Notification+Brownfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE76F2FB499C700E407DE /* Notification+Brownfield.swift */; };
13
+ 79FEE7772FB499C700E407DE /* ReactNativeBrownfieldModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE7732FB499C700E407DE /* ReactNativeBrownfieldModule.swift */; };
14
+ 79FEE7782FB499C700E407DE /* JSBundleLoadObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE76E2FB499C700E407DE /* JSBundleLoadObserver.swift */; };
15
+ 79FEE7792FB499C700E407DE /* ReactNativeBrownfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE7702FB499C700E407DE /* ReactNativeBrownfield.swift */; };
16
+ 79FEE77D2FB499E400E407DE /* ReactNativeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE77B2FB499E400E407DE /* ReactNativeView.swift */; };
17
+ 79FEE77E2FB499E400E407DE /* ReactNativeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE77C2FB499E400E407DE /* ReactNativeViewController.swift */; };
18
+ 79FEE7802FB499EC00E407DE /* ReactNativeHostRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE77F2FB499EC00E407DE /* ReactNativeHostRuntime.swift */; };
19
+ 79FEE7822FB499F300E407DE /* ExpoHostRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FEE7812FB499F300E407DE /* ExpoHostRuntime.swift */; };
14
20
  /* End PBXBuildFile section */
15
21
 
16
22
  /* Begin PBXCopyFilesBuildPhase section */
@@ -27,14 +33,17 @@
27
33
 
28
34
  /* Begin PBXFileReference section */
29
35
  696CB229230EF910000207AF /* libReactNativeBrownfield.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactNativeBrownfield.a; sourceTree = BUILT_PRODUCTS_DIR; };
30
- 696CB241230EF9EA000207AF /* ReactNativeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReactNativeViewController.h; sourceTree = "<group>"; };
31
- 696CB242230EF9EA000207AF /* ReactNativeBrownfieldModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReactNativeBrownfieldModule.h; sourceTree = "<group>"; };
32
- 696CB243230EF9EA000207AF /* ReactNativeBrownfieldNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativeBrownfieldNotifications.m; sourceTree = "<group>"; };
33
- 696CB244230EF9EA000207AF /* ReactNativeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativeViewController.m; sourceTree = "<group>"; };
34
- 696CB245230EF9EA000207AF /* ReactNativeBrownfield.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativeBrownfield.m; sourceTree = "<group>"; };
35
- 696CB246230EF9EA000207AF /* ReactNativeBrownfieldModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReactNativeBrownfieldModule.m; sourceTree = "<group>"; };
36
- 696CB247230EF9EA000207AF /* ReactNativeBrownfieldNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReactNativeBrownfieldNotifications.h; sourceTree = "<group>"; };
37
- 696CB248230EF9EA000207AF /* ReactNativeBrownfield.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReactNativeBrownfield.h; sourceTree = "<group>"; };
36
+ 79FEE76D2FB499C700E407DE /* BrownfieldBundlePathResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrownfieldBundlePathResolver.swift; sourceTree = "<group>"; };
37
+ 79FEE76E2FB499C700E407DE /* JSBundleLoadObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSBundleLoadObserver.swift; sourceTree = "<group>"; };
38
+ 79FEE76F2FB499C700E407DE /* Notification+Brownfield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Brownfield.swift"; sourceTree = "<group>"; };
39
+ 79FEE7702FB499C700E407DE /* ReactNativeBrownfield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeBrownfield.swift; sourceTree = "<group>"; };
40
+ 79FEE7712FB499C700E407DE /* ReactNativeBrownfieldModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactNativeBrownfieldModule.h; sourceTree = "<group>"; };
41
+ 79FEE7722FB499C700E407DE /* ReactNativeBrownfieldModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ReactNativeBrownfieldModule.mm; sourceTree = "<group>"; };
42
+ 79FEE7732FB499C700E407DE /* ReactNativeBrownfieldModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeBrownfieldModule.swift; sourceTree = "<group>"; };
43
+ 79FEE77B2FB499E400E407DE /* ReactNativeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeView.swift; sourceTree = "<group>"; };
44
+ 79FEE77C2FB499E400E407DE /* ReactNativeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeViewController.swift; sourceTree = "<group>"; };
45
+ 79FEE77F2FB499EC00E407DE /* ReactNativeHostRuntime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeHostRuntime.swift; sourceTree = "<group>"; };
46
+ 79FEE7812FB499F300E407DE /* ExpoHostRuntime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpoHostRuntime.swift; sourceTree = "<group>"; };
38
47
  /* End PBXFileReference section */
39
48
 
40
49
  /* Begin PBXFrameworksBuildPhase section */
@@ -51,15 +60,17 @@
51
60
  696CB220230EF910000207AF = {
52
61
  isa = PBXGroup;
53
62
  children = (
54
- 696CB248230EF9EA000207AF /* ReactNativeBrownfield.h */,
55
- 696CB245230EF9EA000207AF /* ReactNativeBrownfield.m */,
56
- 696CB242230EF9EA000207AF /* ReactNativeBrownfieldModule.h */,
57
- 696CB246230EF9EA000207AF /* ReactNativeBrownfieldModule.m */,
58
- 696CB247230EF9EA000207AF /* ReactNativeBrownfieldNotifications.h */,
59
- 696CB243230EF9EA000207AF /* ReactNativeBrownfieldNotifications.m */,
60
- 696CB241230EF9EA000207AF /* ReactNativeViewController.h */,
61
- 696CB244230EF9EA000207AF /* ReactNativeViewController.m */,
63
+ 79FEE76C2FB499A800E407DE /* Views */,
64
+ 79FEE76B2FB4998300E407DE /* Vanilla */,
65
+ 79FEE7692FB4997100E407DE /* Expo */,
62
66
  696CB22A230EF910000207AF /* Products */,
67
+ 79FEE76D2FB499C700E407DE /* BrownfieldBundlePathResolver.swift */,
68
+ 79FEE76E2FB499C700E407DE /* JSBundleLoadObserver.swift */,
69
+ 79FEE76F2FB499C700E407DE /* Notification+Brownfield.swift */,
70
+ 79FEE7702FB499C700E407DE /* ReactNativeBrownfield.swift */,
71
+ 79FEE7712FB499C700E407DE /* ReactNativeBrownfieldModule.h */,
72
+ 79FEE7722FB499C700E407DE /* ReactNativeBrownfieldModule.mm */,
73
+ 79FEE7732FB499C700E407DE /* ReactNativeBrownfieldModule.swift */,
63
74
  );
64
75
  sourceTree = "<group>";
65
76
  };
@@ -71,6 +82,31 @@
71
82
  name = Products;
72
83
  sourceTree = "<group>";
73
84
  };
85
+ 79FEE7692FB4997100E407DE /* Expo */ = {
86
+ isa = PBXGroup;
87
+ children = (
88
+ 79FEE7812FB499F300E407DE /* ExpoHostRuntime.swift */,
89
+ );
90
+ path = Expo;
91
+ sourceTree = "<group>";
92
+ };
93
+ 79FEE76B2FB4998300E407DE /* Vanilla */ = {
94
+ isa = PBXGroup;
95
+ children = (
96
+ 79FEE77F2FB499EC00E407DE /* ReactNativeHostRuntime.swift */,
97
+ );
98
+ path = Vanilla;
99
+ sourceTree = "<group>";
100
+ };
101
+ 79FEE76C2FB499A800E407DE /* Views */ = {
102
+ isa = PBXGroup;
103
+ children = (
104
+ 79FEE77B2FB499E400E407DE /* ReactNativeView.swift */,
105
+ 79FEE77C2FB499E400E407DE /* ReactNativeViewController.swift */,
106
+ );
107
+ path = Views;
108
+ sourceTree = "<group>";
109
+ };
74
110
  /* End PBXGroup section */
75
111
 
76
112
  /* Begin PBXNativeTarget section */
@@ -102,6 +138,7 @@
102
138
  TargetAttributes = {
103
139
  696CB228230EF910000207AF = {
104
140
  CreatedOnToolsVersion = 10.2.1;
141
+ LastSwiftMigration = 2650;
105
142
  };
106
143
  };
107
144
  };
@@ -127,10 +164,16 @@
127
164
  isa = PBXSourcesBuildPhase;
128
165
  buildActionMask = 2147483647;
129
166
  files = (
130
- 696CB24C230EF9EA000207AF /* ReactNativeBrownfieldModule.m in Sources */,
131
- 696CB24B230EF9EA000207AF /* ReactNativeBrownfield.m in Sources */,
132
- 696CB249230EF9EA000207AF /* ReactNativeBrownfieldNotifications.m in Sources */,
133
- 696CB24A230EF9EA000207AF /* ReactNativeViewController.m in Sources */,
167
+ 79FEE7802FB499EC00E407DE /* ReactNativeHostRuntime.swift in Sources */,
168
+ 79FEE7742FB499C700E407DE /* BrownfieldBundlePathResolver.swift in Sources */,
169
+ 79FEE7752FB499C700E407DE /* ReactNativeBrownfieldModule.mm in Sources */,
170
+ 79FEE7762FB499C700E407DE /* Notification+Brownfield.swift in Sources */,
171
+ 79FEE7822FB499F300E407DE /* ExpoHostRuntime.swift in Sources */,
172
+ 79FEE7772FB499C700E407DE /* ReactNativeBrownfieldModule.swift in Sources */,
173
+ 79FEE7782FB499C700E407DE /* JSBundleLoadObserver.swift in Sources */,
174
+ 79FEE7792FB499C700E407DE /* ReactNativeBrownfield.swift in Sources */,
175
+ 79FEE77D2FB499E400E407DE /* ReactNativeView.swift in Sources */,
176
+ 79FEE77E2FB499E400E407DE /* ReactNativeViewController.swift in Sources */,
134
177
  );
135
178
  runOnlyForDeploymentPostprocessing = 0;
136
179
  };
@@ -252,11 +295,15 @@
252
295
  696CB233230EF910000207AF /* Debug */ = {
253
296
  isa = XCBuildConfiguration;
254
297
  buildSettings = {
298
+ CLANG_ENABLE_MODULES = YES;
255
299
  CODE_SIGN_STYLE = Automatic;
256
300
  DEVELOPMENT_TEAM = PJL7DA2X5F;
257
301
  OTHER_LDFLAGS = "-ObjC";
258
302
  PRODUCT_NAME = "$(TARGET_NAME)";
259
303
  SKIP_INSTALL = YES;
304
+ SWIFT_OBJC_BRIDGING_HEADER = "ReactNativeBrownfield-Bridging-Header.h";
305
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
306
+ SWIFT_VERSION = 6.0;
260
307
  TARGETED_DEVICE_FAMILY = "1,2";
261
308
  };
262
309
  name = Debug;
@@ -264,11 +311,14 @@
264
311
  696CB234230EF910000207AF /* Release */ = {
265
312
  isa = XCBuildConfiguration;
266
313
  buildSettings = {
314
+ CLANG_ENABLE_MODULES = YES;
267
315
  CODE_SIGN_STYLE = Automatic;
268
316
  DEVELOPMENT_TEAM = PJL7DA2X5F;
269
317
  OTHER_LDFLAGS = "-ObjC";
270
318
  PRODUCT_NAME = "$(TARGET_NAME)";
271
319
  SKIP_INSTALL = YES;
320
+ SWIFT_OBJC_BRIDGING_HEADER = "ReactNativeBrownfield-Bridging-Header.h";
321
+ SWIFT_VERSION = 6.0;
272
322
  TARGETED_DEVICE_FAMILY = "1,2";
273
323
  };
274
324
  name = Release;
@@ -1,3 +1,4 @@
1
+ #if !canImport(Expo)
1
2
  import UIKit
2
3
  internal import React
3
4
  internal import React_RCTAppDelegate
@@ -7,6 +8,7 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
7
8
  var entryFile = "index"
8
9
  var bundlePath = "main.jsbundle"
9
10
  var bundle = Bundle.main
11
+ var preferEmbeddedBundleInDebug = false
10
12
  var bundleURLOverride: (() -> URL?)? = nil
11
13
  // MARK: - RCTReactNativeFactoryDelegate Methods
12
14
 
@@ -15,23 +17,27 @@ class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
15
17
  }
16
18
 
17
19
  public override func bundleURL() -> URL? {
18
- if let bundleURLProvider = bundleURLOverride {
19
- return bundleURLProvider()
20
- }
21
-
22
- #if DEBUG
23
- return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: entryFile)
24
- #else
25
20
  do {
26
- let (resourceName, fileExtension) = try BrownfieldBundlePathResolver.resourceComponents(
27
- from: bundlePath
21
+ #if DEBUG
22
+ let isDebug = true
23
+ #else
24
+ let isDebug = false
25
+ #endif
26
+
27
+ return try BrownfieldBundleURLResolver.resolve(
28
+ isDebug: isDebug,
29
+ preferEmbeddedBundleInDebug: preferEmbeddedBundleInDebug,
30
+ bundlePath: bundlePath,
31
+ bundle: bundle,
32
+ bundleURLOverride: bundleURLOverride,
33
+ metroURL: {
34
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: entryFile)
35
+ }
28
36
  )
29
- return bundle.url(forResource: resourceName, withExtension: fileExtension)
30
37
  } catch {
31
38
  assertionFailure("Invalid bundlePath '\(bundlePath)': \(error)")
32
39
  return nil
33
40
  }
34
- #endif
35
41
  }
36
42
  }
37
43
 
@@ -40,6 +46,15 @@ final class ReactNativeHostRuntime {
40
46
  private let jsBundleLoadObserver = JSBundleLoadObserver()
41
47
  private var delegate = ReactNativeBrownfieldDelegate()
42
48
 
49
+ private func configureDevLoadingView() {
50
+ #if DEBUG
51
+ let shouldDisableDevLoadingView =
52
+ preferEmbeddedBundleInDebug && (delegate.bundleURL()?.isFileURL ?? false)
53
+
54
+ BrownfieldDevLoadingViewBridge.setEnabled(!shouldDisableDevLoadingView)
55
+ #endif
56
+ }
57
+
43
58
  /**
44
59
  * Path to JavaScript root.
45
60
  * Default value: "index"
@@ -70,6 +85,16 @@ final class ReactNativeHostRuntime {
70
85
  }
71
86
  }
72
87
 
88
+ /**
89
+ * Prefer the embedded JavaScript bundle instead of Metro when this framework is built in Debug.
90
+ * Default value: false
91
+ */
92
+ public var preferEmbeddedBundleInDebug: Bool = false {
93
+ didSet {
94
+ delegate.preferEmbeddedBundleInDebug = preferEmbeddedBundleInDebug
95
+ }
96
+ }
97
+
73
98
  /**
74
99
  * Dynamic bundle URL provider called on every bundle load.
75
100
  * When set, this overrides the default bundleURL() behavior in the delegate.
@@ -112,7 +137,9 @@ final class ReactNativeHostRuntime {
112
137
  initialProps: [AnyHashable: Any]?,
113
138
  launchOptions: [AnyHashable: Any]? = nil
114
139
  ) -> UIView? {
115
- reactNativeFactory?.rootViewFactory.view(
140
+ configureDevLoadingView()
141
+
142
+ return reactNativeFactory?.rootViewFactory.view(
116
143
  withModuleName: moduleName,
117
144
  initialProperties: initialProps,
118
145
  launchOptions: launchOptions
@@ -171,3 +198,4 @@ final class ReactNativeHostRuntime {
171
198
  }
172
199
  }
173
200
  }
201
+ #endif
@@ -0,0 +1,27 @@
1
+ // swift-tools-version: 5.9
2
+
3
+ import PackageDescription
4
+
5
+ let package = Package(
6
+ name: "BrownfieldBundleSupport",
7
+ platforms: [
8
+ .macOS(.v13),
9
+ ],
10
+ products: [
11
+ .library(
12
+ name: "BrownfieldBundleSupport",
13
+ targets: ["BrownfieldBundleSupport"]
14
+ ),
15
+ ],
16
+ targets: [
17
+ .target(
18
+ name: "BrownfieldBundleSupport",
19
+ path: "Sources/BrownfieldBundleSupport"
20
+ ),
21
+ .testTarget(
22
+ name: "BrownfieldBundleSupportTests",
23
+ dependencies: ["BrownfieldBundleSupport"],
24
+ path: "Tests/BrownfieldBundleSupportTests"
25
+ ),
26
+ ]
27
+ )
@@ -0,0 +1,28 @@
1
+ import Foundation
2
+
3
+ final class BrownfieldBundleURLResolver {
4
+ private init() {}
5
+
6
+ static func resolve(
7
+ isDebug: Bool,
8
+ preferEmbeddedBundleInDebug: Bool,
9
+ bundlePath: String,
10
+ bundle: Bundle,
11
+ bundleURLOverride: (() -> URL?)?,
12
+ metroURL: () -> URL?
13
+ ) throws -> URL? {
14
+ if let overriddenURL = bundleURLOverride?() {
15
+ return overriddenURL
16
+ }
17
+
18
+ if isDebug && !preferEmbeddedBundleInDebug {
19
+ return metroURL()
20
+ }
21
+
22
+ let (resourceName, fileExtension) = try BrownfieldBundlePathResolver.resourceComponents(
23
+ from: bundlePath
24
+ )
25
+
26
+ return bundle.url(forResource: resourceName, withExtension: fileExtension)
27
+ }
28
+ }