Skip to content

Commit c46e60a

Browse files
committed
feat: support packaging as a framework
1 parent 2c12594 commit c46e60a

7 files changed

Lines changed: 88 additions & 66 deletions

ReactBrownfield.podspec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ Pod::Spec.new do |spec|
1414

1515
spec.module_name = "ReactBrownfield"
1616
spec.source = { :git => "git@github.com:callstack/react-native-brownfield.git", :tag => "#{spec.version}" }
17-
spec.source_files = "ios/**/*.{h,m,mm,swift}"
17+
spec.source_files = "ios/**/*.{swift,h,m,mm}"
1818
spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
1919

2020
spec.dependency 'ReactAppDependencyProvider'
21+
add_dependency(spec, "React-RCTAppDelegate")
22+
2123
install_modules_dependencies(spec)
2224
end

example/swift/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ target 'SwiftExample' do
2828
installer,
2929
config[:reactNativePath],
3030
:mac_catalyst_enabled => false,
31-
:ccache_enabled => true
31+
# :ccache_enabled => true
3232
)
3333
end
3434
end

example/swift/Podfile.lock

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,7 +1527,7 @@ PODS:
15271527
- React-jsi (= 0.78.0)
15281528
- ReactAppDependencyProvider (0.78.0):
15291529
- ReactCodegen
1530-
- ReactBrownfield (1.0.0-rc.0):
1530+
- ReactBrownfield (1.0.0-rc.1):
15311531
- DoubleConversion
15321532
- glog
15331533
- hermes-engine
@@ -1541,6 +1541,7 @@ PODS:
15411541
- React-graphics
15421542
- React-ImageManager
15431543
- React-NativeModulesApple
1544+
- React-RCTAppDelegate
15441545
- React-RCTFabric
15451546
- React-rendererdebug
15461547
- React-utils
@@ -1944,7 +1945,7 @@ SPEC CHECKSUMS:
19441945
React-timing: bb220a53a795ed57976a4855c521f3de2f298fe5
19451946
React-utils: 3b054aaebe658fc710a8d239d0e4b9fd3e0b78f9
19461947
ReactAppDependencyProvider: a1fb08dfdc7ebc387b2e54cfc9decd283ed821d8
1947-
ReactBrownfield: e05f198df083698ed9942ace80fd90da6e9298de
1948+
ReactBrownfield: 5f7bd48686a1ea3e4d2ef03bfa7f719c2d03bdad
19481949
ReactCodegen: 008c319179d681a6a00966edfc67fda68f9fbb2e
19491950
ReactCommon: 0c097b53f03d6bf166edbcd0915da32f3015dd90
19501951
RNScreens: 0d4cb9afe052607ad0aa71f645a88bb7c7f2e64c

ios/ReactNativeBrownfield.swift

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,104 @@
1-
import React
2-
import React_RCTAppDelegate
3-
import ReactAppDependencyProvider
1+
import UIKit
2+
internal import React
3+
internal import React_RCTAppDelegate
4+
internal import ReactAppDependencyProvider
45

5-
@objc public class ReactNativeBrownfield: RCTDefaultReactNativeFactoryDelegate {
6-
@objc public static let shared = ReactNativeBrownfield()
6+
class ReactNativeBrownfieldDelegate: RCTDefaultReactNativeFactoryDelegate {
7+
var entryFile = "index"
8+
9+
// MARK: - RCTReactNativeFactoryDelegate Methods
10+
override func sourceURL(for bridge: RCTBridge) -> URL? {
11+
return bundleURL()
12+
}
13+
14+
public override func bundleURL() -> URL? {
15+
#if DEBUG
16+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: entryFile)
17+
#else
18+
let resourceURLComponents = bundlePath.components(separatedBy: ".")
19+
let withoutLast = resourceURLComponents[..<(resourceURLComponents.count - 1)]
20+
let resourceName = withoutLast.joined()
21+
let fileExtension = resourceURLComponents.last ?? ""
22+
23+
return Bundle.main.url(forResource: resourceName, withExtension: fileExtension)
24+
#endif
25+
}
26+
}
27+
28+
public class ReactNativeBrownfield {
29+
public static let shared = ReactNativeBrownfield()
730
private var onBundleLoaded: (() -> Void)?
31+
private var delegate = ReactNativeBrownfieldDelegate()
832

933
/**
1034
* Path to JavaScript root.
1135
* Default value: "index"
1236
*/
13-
@objc public var entryFile: String = "index"
37+
public var entryFile: String = "index" {
38+
didSet {
39+
delegate.entryFile = entryFile
40+
}
41+
}
1442
/**
1543
* Path to bundle fallback resource.
1644
* Default value: nil
1745
*/
18-
@objc public var fallbackResource: String? = nil
46+
public var fallbackResource: String? = nil
1947
/**
2048
* Path to JavaScript bundle file.
2149
* Default value: "main.jsbundle"
2250
*/
23-
@objc public var bundlePath: String = "main.jsbundle"
51+
public var bundlePath: String = "main.jsbundle"
2452
/**
2553
* React Native factory instance created when starting React Native.
2654
* Default value: nil
2755
*/
28-
@objc public var reactNativeFactory: RCTReactNativeFactory? = nil
56+
private var reactNativeFactory: RCTReactNativeFactory? = nil
2957
/**
3058
* Root view factory used to create React Native views.
3159
*/
32-
@objc lazy public var rootViewFactory: RCTRootViewFactory? = {
60+
lazy private var rootViewFactory: RCTRootViewFactory? = {
3361
return reactNativeFactory?.rootViewFactory
3462
}()
3563

3664
/**
3765
* Starts React Native with default parameters.
3866
*/
39-
@objc public func startReactNative() {
67+
public func startReactNative() {
4068
startReactNative(onBundleLoaded: nil)
4169
}
42-
70+
71+
public func view(
72+
moduleName: String, initialProps: [AnyHashable: Any]?,
73+
launchOptions: [AnyHashable: Any]?
74+
) -> UIView? {
75+
reactNativeFactory?.rootViewFactory.view(
76+
withModuleName: moduleName, initialProperties: initialProps,
77+
launchOptions: launchOptions
78+
)
79+
}
80+
4381
/**
4482
* Starts React Native with optional callback when bundle is loaded.
45-
*
83+
*
4684
* @param onBundleLoaded Optional callback invoked after JS bundle is fully loaded.
4785
*/
48-
@objc public func startReactNative(onBundleLoaded: (() -> Void)?) {
86+
public func startReactNative(onBundleLoaded: (() -> Void)?) {
4987
startReactNative(onBundleLoaded: onBundleLoaded, launchOptions: nil)
5088
}
51-
89+
5290
/**
5391
* Starts React Native with optional callback and launch options.
54-
*
92+
*
5593
* @param onBundleLoaded Optional callback invoked after JS bundle is fully loaded.
5694
* @param launchOptions Launch options, typically passed from AppDelegate.
5795
*/
58-
@objc public func startReactNative(onBundleLoaded: (() -> Void)?, launchOptions: [AnyHashable: Any]?) {
96+
public func startReactNative(onBundleLoaded: (() -> Void)?, launchOptions: [AnyHashable: Any]?) {
5997
guard reactNativeFactory == nil else { return }
60-
61-
self.dependencyProvider = RCTAppDependencyProvider()
62-
self.reactNativeFactory = RCTReactNativeFactory(delegate: self)
63-
98+
99+
delegate.dependencyProvider = RCTAppDependencyProvider()
100+
self.reactNativeFactory = RCTReactNativeFactory(delegate: delegate)
101+
64102
if let onBundleLoaded {
65103
self.onBundleLoaded = onBundleLoaded
66104
if RCTIsNewArchEnabled() {
@@ -80,31 +118,14 @@ import ReactAppDependencyProvider
80118
}
81119
}
82120
}
83-
121+
84122
@objc private func jsLoaded(_ notification: Notification) {
85123
onBundleLoaded?()
86124
onBundleLoaded = nil
87125
NotificationCenter.default.removeObserver(self)
88126
}
89-
90-
// MARK: - RCTReactNativeFactoryDelegate Methods
91-
92-
@objc public override func sourceURL(for bridge: RCTBridge) -> URL? {
93-
return bundleURL()
94-
}
95-
96-
public override func bundleURL() -> URL? {
97-
#if DEBUG
98-
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: entryFile)
99-
#else
100-
let resourceURLComponents = bundlePath.components(separatedBy: ".")
101-
let withoutLast = resourceURLComponents[..<(resourceURLComponents.count - 1)]
102-
let resourceName = withoutLast.joined()
103-
let fileExtension = resourceURLComponents.last ?? ""
104-
105-
return Bundle.main.url(forResource: resourceName, withExtension: fileExtension)
106-
#endif
107-
}
127+
128+
108129
}
109130

110131
extension Notification.Name {

ios/ReactNativeBrownfieldModule.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#ifdef __cplusplus
22

3-
#import <React-RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate.h>
43
#import <ReactNativeBrownfield/ReactNativeBrownfield.h>
54

65
@interface ReactNativeBrownfieldModule : NSObject <NativeReactNativeBrownfieldModuleSpec>

ios/ReactNativeBrownfieldModule.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React
1+
internal import React
22

33
@objcMembers
44
public class ReactNativeBrownfieldModuleImpl: NSObject {
@@ -8,7 +8,7 @@ public class ReactNativeBrownfieldModuleImpl: NSObject {
88
NotificationCenter.default.post(name: Notification.Name.togglePopGestureRecognizer, object: nil, userInfo: userInfo)
99
}
1010
}
11-
11+
1212
static public func popToNative(animated: Bool) {
1313
let userInfo = ["animated": animated]
1414
DispatchQueue.main.async {

ios/ReactNativeViewController.swift

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,37 @@
11
import UIKit
2-
import React
2+
internal import React
33

44
@objc public class ReactNativeViewController: UIViewController {
55
private var moduleName: String
66
private var initialProperties: [String: Any]?
7-
7+
88
@objc public init(moduleName: String, initialProperties: [String: Any]? = nil) {
99
self.moduleName = moduleName
1010
self.initialProperties = initialProperties
1111
super.init(nibName: nil, bundle: nil)
1212
}
13-
13+
1414
required init?(coder: NSCoder) {
1515
fatalError("init(coder:) has not been implemented")
1616
}
17-
17+
1818
public override func viewDidLoad() {
1919
super.viewDidLoad()
20-
21-
guard let factory = ReactNativeBrownfield.shared.rootViewFactory else {
22-
print("Error: You need to start React Native in order to use ReactNativeViewController, make sure to run BridgeManager.shared.startReactNative() before instantiating it.")
23-
return
24-
}
25-
20+
2621
if !moduleName.isEmpty {
27-
view = factory.view(withModuleName: moduleName, initialProperties: initialProperties)
28-
22+
view = ReactNativeBrownfield.shared.view(
23+
moduleName: moduleName,
24+
initialProps: initialProperties,
25+
launchOptions: nil
26+
)
27+
2928
NotificationCenter.default.addObserver(
3029
self,
3130
selector: #selector(togglePopGestureRecognizer(_:)),
3231
name: NSNotification.Name.togglePopGestureRecognizer,
3332
object: nil
3433
)
35-
34+
3635
NotificationCenter.default.addObserver(
3736
self,
3837
selector: #selector(popToNative(_:)),
@@ -41,24 +40,24 @@ import React
4140
)
4241
}
4342
}
44-
43+
4544
deinit {
4645
NotificationCenter.default.removeObserver(self)
4746
}
48-
47+
4948
@objc private func togglePopGestureRecognizer(_ notification: Notification) {
5049
guard let userInfo = notification.userInfo,
5150
let enabled = userInfo["enabled"] as? Bool else { return }
52-
51+
5352
DispatchQueue.main.async { [weak self] in
5453
self?.navigationController?.interactivePopGestureRecognizer?.isEnabled = enabled
5554
}
5655
}
57-
56+
5857
@objc private func popToNative(_ notification: Notification) {
5958
guard let userInfo = notification.userInfo,
6059
let animated = userInfo["animated"] as? Bool else { return }
61-
60+
6261
DispatchQueue.main.async { [weak self] in
6362
self?.navigationController?.popViewController(animated: animated)
6463
}

0 commit comments

Comments
 (0)