Skip to content

Commit 0ca5f66

Browse files
authored
feat: SwiftUI support (#93)
* feat: SwiftUI support * feat: better code documentation
1 parent f98697d commit 0ca5f66

13 files changed

+304
-207
lines changed

docs/OBJECTIVE_C.md

+14-21
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ You can import the object from:
5656

5757
`shared`
5858

59-
A singleton that keeps an instance of ReactNativeBrownfield object.
59+
A singleton that keeps an instance of `ReactNativeBrownfield` object.
6060

6161
Examples:
6262

@@ -70,10 +70,10 @@ Examples:
7070

7171
| Property | Type | Default | Description |
7272
| -------------------------- | ----------------------- | -------------- | -------------------------------------------------- |
73-
| entryFile | NSString | index | Path to JavaScript root. |
74-
| fallbackResource | NSString | nil | Path to bundle fallback resource. |
75-
| bundlePath | NSString | main.jsbundle | Path to bundle fallback resource. |
76-
| reactNativeFactory | RCTReactNativeFactory | nil | React Native factory instance. |
73+
| `entryFile` | `NSString` | `index` | Path to JavaScript root. |
74+
| `fallbackResource` | `NSString` | `nil` | Path to bundle fallback resource. |
75+
| `bundlePath` | `NSString` | `main.jsbundle`| Path to bundle fallback resource. |
76+
| `reactNativeFactory` | `RCTReactNativeFactory` | `nil` | React Native factory instance. |
7777

7878
---
7979

@@ -85,10 +85,10 @@ Starts React Native, produces an instance of a bridge. You can use it to initial
8585

8686
Params:
8787

88-
| Param | Required | Type | Description |
89-
| ----------------------- | -------- | ------------- | ----------------------------------------------------- |
90-
| onBundleLoaded | No | void(^)(void) | Callback invoked after JS bundle is fully loaded. |
91-
| launchOptions | No | NSDictionary | Launch options, typically passed from AppDelegate. |
88+
| Param | Required | Type | Description |
89+
| ----------------------- | -------- | ----------------- | ----------------------------------------------------- |
90+
| `onBundleLoaded` | No | `void(^)(void)` | Callback invoked after JS bundle is fully loaded. |
91+
| `launchOptions` | No | `NSDictionary` | Launch options, typically passed from AppDelegate. |
9292

9393
Examples:
9494

@@ -110,7 +110,7 @@ Examples:
110110

111111
---
112112

113-
#### ReactNativeViewController
113+
#### `ReactNativeViewController`
114114

115115
A view controller that's rendering React Native view within its bounds. It automatically uses an instance of a factory created in `startReactNative` method. It works well with exposed JavaScript module. It's the simplest way to embed React Native into your navigation stack.
116116

@@ -126,10 +126,10 @@ You can import it from:
126126

127127
`[ReactNativeViewController initWithModuleName:moduleName andInitialProperties:initialProps]`
128128

129-
| Param | Required | Type | Description |
130-
| ------------------ | --------- | ------------- | ------------------------------------------------------------- |
131-
| moduleName | Yes | NSString | Name of React Native component registered to `AppRegistry`. |
132-
| initialProperties | No | NSDictionary | Initial properties to be passed to React Native component. |
129+
| Param | Required | Type | Description |
130+
| --------------------- | --------- | --------------- | ------------------------------------------------------------- |
131+
| `moduleName` | Yes | `NSString` | Name of React Native component registered to `AppRegistry`. |
132+
| `initialProperties` | No | `NSDictionary` | Initial properties to be passed to React Native component. |
133133

134134
Examples:
135135

@@ -141,10 +141,3 @@ Examples:
141141
[[ReactNativeViewController alloc] initWithModuleName:@"ReactNative" andInitialProperties:@{@"score": @12}]
142142
```
143143

144-
---
145-
146-
### Example
147-
148-
You can find an example app [here](../example/objc).
149-
150-

docs/SWIFT.md

+146-30
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,6 @@
22

33
React Native Brownfield provides first-class support for Swift.
44

5-
### `use_frameworks!` support
6-
7-
It is possible to build `react-native-brownfield` with `use_frameworks!` directive in CocoaPods as long as `React` can be built this way.
8-
9-
| React Native version | `use_frameworks!` compatibility |
10-
| -------------------------- | ------------------------------- |
11-
| <= 0.59.X | Compatible |
12-
| 0.60.X | Not compatible |
13-
| 0.61.0-rc.0 | Not compatible |
14-
15-
Please reffer to [this issue](https://github.com/facebook/react-native/issues/25349) to learn more about `use_frameworks!` state in React Native.
16-
17-
Until this behavior is fixed, you can access `react-native-brownfield` API in Swift via [Bridging Header](../example/swift/BridgingHeader.h).
18-
195
### Linking
206

217
The library is meant to work with [auto linking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md). In case you can't use this feature, please check out the following options:
@@ -56,7 +42,7 @@ Click on your main project file (the one that represents the `.xcodeproj`) selec
5642

5743
### API Reference
5844

59-
#### ReactNativeBrownfield
45+
#### `ReactNativeBrownfield`
6046

6147
You can import the object from:
6248

@@ -70,7 +56,7 @@ You can import the object from:
7056

7157
`shared`
7258

73-
A singleton that keeps an instance of ReactNativeBrownfield object.
59+
A singleton that keeps an instance of `ReactNativeBrownfield` object.
7460

7561
Examples:
7662

@@ -84,25 +70,25 @@ Examples:
8470

8571
| Property | Type | Default | Description |
8672
| -------------------------- | ----------------------- | -------------- | -------------------------------------------------- |
87-
| entryFile | String | index | Path to JavaScript root. |
88-
| fallbackResource | String? | nil | Path to bundle fallback resource. |
89-
| bundlePath | String | main.jsbundle | Path to bundle fallback resource. |
90-
| reactNativeFactory | RCTReactNativeFactory? | nil | React Native factory instance. |
73+
| `entryFile` | `String` | index | Path to JavaScript root. |
74+
| `fallbackResource` | `String?` | nil | Path to bundle fallback resource. |
75+
| `bundlePath` | `String` | main.jsbundle | Path to bundle fallback resource. |
76+
| `reactNativeFactory` | `RCTReactNativeFactory?` | nil | React Native factory instance. |
9177

9278
---
9379

9480
**Methods:**
9581

9682
`startReactNative`
9783

98-
Starts React Native, produces an instance of a bridge. You can use it to initialize React Native in your app.
84+
Starts React Native. You can use it to initialize React Native in your app.
9985

10086
Params:
10187

102-
| Param | Required | Type | Description |
103-
| ----------------------- | -------- | ------------- | ----------------------------------------------------- |
104-
| onBundleLoaded | No | (() -> Void)? | Callback invoked after JS bundle is fully loaded. |
105-
| launchOptions | No | [AnyHashable: Any]? | Launch options, typically passed from AppDelegate. |
88+
| Param | Required | Type | Description |
89+
| ----------------------- | -------- | ------------------- | ----------------------------------------------------- |
90+
| `onBundleLoaded` | No | `(() -> Void)?` | Callback invoked after JS bundle is fully loaded. |
91+
| `launchOptions` | No | `[AnyHashable: Any]?` | Launch options, typically passed from AppDelegate. |
10692

10793
Examples:
10894

@@ -124,7 +110,95 @@ Examples:
124110

125111
---
126112

127-
#### ReactNativeViewController
113+
#### Initialization Approaches
114+
115+
React Native Brownfield supports two main approaches for initialization:
116+
117+
**1. UIKit AppDelegate Approach (Traditional)**
118+
119+
```swift
120+
import UIKit
121+
import ReactNativeBrownfield
122+
123+
class AppDelegate: UIResponder, UIApplicationDelegate {
124+
var window: UIWindow?
125+
126+
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
127+
ReactNativeBrownfield.shared.startReactNative {
128+
print("React Native bundle loaded")
129+
}
130+
return true
131+
}
132+
}
133+
```
134+
135+
To present a React Native view in a UIKit app, use `ReactNativeViewController`:
136+
137+
```swift
138+
import UIKit
139+
import ReactNativeBrownfield
140+
141+
class ViewController: UIViewController {
142+
@IBAction func openReactNativeScreen(_ sender: UIButton) {
143+
let reactNativeVC = ReactNativeViewController(moduleName: "ReactNative")
144+
145+
present(reactNativeVC, animated: true)
146+
}
147+
}
148+
```
149+
150+
**2. SwiftUI App Approach (Modern)**
151+
152+
```swift
153+
import SwiftUI
154+
import ReactNativeBrownfield
155+
156+
@main
157+
struct MyApp: App {
158+
init() {
159+
ReactNativeBrownfield.shared.startReactNative {
160+
print("React Native bundle loaded")
161+
}
162+
}
163+
164+
var body: some Scene {
165+
WindowGroup {
166+
ContentView()
167+
}
168+
}
169+
}
170+
```
171+
172+
To display React Native views in SwiftUI, use the provided `ReactNativeView` component:
173+
174+
```swift
175+
import SwiftUI
176+
import ReactNativeBrownfield
177+
178+
struct ContentView: View {
179+
var body: some View {
180+
NavigationView {
181+
VStack {
182+
Text("Welcome to the Native App")
183+
.padding()
184+
185+
NavigationLink("Push React Native Screen") {
186+
ReactNativeView(moduleName: "ReactNative")
187+
.navigationBarHidden(true)
188+
}
189+
}
190+
}
191+
}
192+
}
193+
```
194+
195+
The `ReactNativeView` component is a SwiftUI wrapper around `ReactNativeViewController` that handles navigation and lifecycle events automatically.
196+
197+
Both approaches achieve the same result - initializing React Native when your app launches and presenting React Native screens when needed. Choose the approach that best fits your app architecture.
198+
199+
---
200+
201+
#### `ReactNativeViewController`
128202

129203
A view controller that's rendering React Native view within its bounds. It automatically uses an instance of a factory created in `startReactNative` method. It works well with exposed JavaScript module. It's the simplest way to embed React Native into your navigation stack.
130204

@@ -140,10 +214,10 @@ You can import it from:
140214

141215
`ReactNativeViewController(moduleName: moduleName, initialProperties: initialProperties)`
142216

143-
| Param | Required | Type | Description |
144-
| ------------------ | --------- | ------------- | ------------------------------------------------------------- |
145-
| moduleName | Yes | String | Name of React Native component registered to `AppRegistry`. |
146-
| initialProperties | No | [String: Any]? | Initial properties to be passed to React Native component. |
217+
| Param | Required | Type | Description |
218+
| ------------------ | --------- | --------------- | ------------------------------------------------------------- |
219+
| `moduleName` | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
220+
| `initialProperties`| No | `[String: Any]?`| Initial properties to be passed to React Native component. |
147221

148222
Examples:
149223

@@ -157,6 +231,48 @@ Examples:
157231

158232
---
159233

234+
#### `ReactNativeView`
235+
236+
A SwiftUI view that wraps the `ReactNativeViewController`, making it easy to integrate React Native into SwiftUI navigation flows. It automatically handles navigation events like "pop to native" from React Native.
237+
238+
You can import it from:
239+
240+
```swift
241+
import ReactNativeBrownfield
242+
```
243+
244+
---
245+
246+
**Constructors:**
247+
248+
`ReactNativeView(moduleName: moduleName, initialProperties: initialProperties)`
249+
250+
| Param | Required | Type | Description |
251+
| ------------------- | --------- | -------------- | ------------------------------------------------------------- |
252+
| `moduleName` | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
253+
| `initialProperties` | No | `[String: Any]`| Initial properties to be passed to React Native component. |
254+
255+
Examples:
256+
257+
```swift
258+
ReactNativeView(moduleName: "ReactNative")
259+
```
260+
261+
```swift
262+
ReactNativeView(moduleName: "ReactNative", initialProperties: ["score": 12])
263+
```
264+
265+
Usage with SwiftUI navigation:
266+
267+
```swift
268+
NavigationLink("Open React Native Screen") {
269+
ReactNativeView(moduleName: "ReactNative")
270+
.navigationBarHidden(true)
271+
}
272+
```
273+
274+
---
275+
160276
### Example
161277

162278
You can find an example app [here](../example/swift).

example/swift/App.swift

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import SwiftUI
2+
import ReactNativeBrownfield
3+
4+
@main
5+
struct MyApp: App {
6+
init() {
7+
ReactNativeBrownfield.shared.startReactNative {
8+
print("loaded")
9+
}
10+
}
11+
12+
var body: some Scene {
13+
WindowGroup {
14+
ContentView()
15+
}
16+
}
17+
}
18+
19+
struct ContentView: View {
20+
var body: some View {
21+
NavigationView {
22+
VStack {
23+
Text("React Native Brownfield App")
24+
.font(.title)
25+
.bold()
26+
.padding()
27+
28+
NavigationLink("Push React Native Screen") {
29+
ReactNativeView(moduleName: "ReactNative")
30+
.navigationBarHidden(true)
31+
}
32+
}
33+
}
34+
}
35+
}

example/swift/AppDelegate.swift

-15
This file was deleted.

0 commit comments

Comments
 (0)