Skip to content

Commit fbff43b

Browse files
committed
Merge branch 'main' into feat/cli
2 parents 7f96502 + ac6decb commit fbff43b

File tree

14 files changed

+930
-78
lines changed

14 files changed

+930
-78
lines changed

apps/TesterIntegrated/swift/App.swift

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ struct MyApp: App {
5555
}
5656

5757
struct FullScreenView: View {
58-
@UseStore<BrownfieldStore> var store
59-
6058
var body: some View {
6159
NavigationView {
6260
VStack {
@@ -66,19 +64,8 @@ struct MyApp: App {
6664
.padding()
6765
.multilineTextAlignment(.center)
6866

69-
Text("Count: \(Int(store.state.counter))")
70-
71-
TextField("Name", text: Binding(get: { store.state.user.name }, set: { data in
72-
store.set { $0.user.name = data }
73-
}))
74-
.textFieldStyle(.roundedBorder)
75-
.padding(.horizontal)
76-
77-
Button("Increment") {
78-
store.set { $0.counter += 1 }
79-
}
80-
.buttonStyle(.borderedProminent)
81-
.padding(.bottom)
67+
CounterView()
68+
UserView()
8269

8370
NavigationLink("Push React Native Screen") {
8471
ReactNativeView(moduleName: "ReactNative")
@@ -94,26 +81,49 @@ struct MyApp: App {
9481
}
9582
}
9683

84+
struct CounterView: View {
85+
@UseStore(\BrownfieldStore.counter) var counter
86+
87+
var body: some View {
88+
VStack {
89+
Text("Count: \(Int(counter))")
90+
Stepper(value: $counter, label: { Text("Increment") })
91+
92+
.buttonStyle(.borderedProminent)
93+
.padding(.bottom)
94+
}
95+
}
96+
}
97+
98+
struct UserView: View {
99+
@UseStore(\BrownfieldStore.user.name) var name
100+
101+
var body: some View {
102+
TextField("Name", text: $name)
103+
.textFieldStyle(.roundedBorder)
104+
.padding(.horizontal)
105+
}
106+
}
107+
97108
struct NativeView: View {
98-
@UseStore<BrownfieldStore> var store
109+
@UseStore(\BrownfieldStore.counter) var counter
110+
@UseStore(\BrownfieldStore.user) var user
99111

100112
var body: some View {
101113
VStack {
102114
Text("Native Side")
103115
.font(.headline)
104116
.padding(.top)
105117

106-
Text("User: \(store.state.user.name)")
107-
Text("Count: \(Int(store.state.counter))")
118+
Text("User: \(user.name)")
119+
Text("Count: \(Int(counter))")
108120

109-
TextField("Name", text: Binding(get: { store.state.user.name }, set: { data in
110-
store.set { $0.user.name = data }
111-
}))
121+
TextField("Name", text: $user.name)
112122
.textFieldStyle(.roundedBorder)
113123
.padding(.horizontal)
114124

115125
Button("Increment") {
116-
store.set { $0.counter += 1 }
126+
$counter.set { $0 + 1 }
117127
}
118128
.buttonStyle(.borderedProminent)
119129

apps/TesterIntegrated/swift/Generated/BrownfieldStore.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,28 @@ import Brownie
55
//
66
// let brownfieldStore = try? JSONDecoder().decode(BrownfieldStore.self, from: jsonData)
77

8+
//
9+
// Hashable or Equatable:
10+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
11+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
12+
// synthesized for types that have collections (such as arrays or dictionaries).
13+
814
import Foundation
915

1016
// MARK: - BrownfieldStore
11-
struct BrownfieldStore: Codable {
17+
struct BrownfieldStore: Codable, Equatable {
1218
var counter: Double
1319
var user: User
1420
}
1521

22+
//
23+
// Hashable or Equatable:
24+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
25+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
26+
// synthesized for types that have collections (such as arrays or dictionaries).
27+
1628
// MARK: - User
17-
struct User: Codable {
29+
struct User: Codable, Equatable {
1830
var name: String
1931
}
2032

apps/TesterIntegrated/swift/Generated/SettingsStore.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import Brownie
55
//
66
// let settingsStore = try? JSONDecoder().decode(SettingsStore.self, from: jsonData)
77

8+
//
9+
// Hashable or Equatable:
10+
// The compiler will not be able to synthesize the implementation of Hashable or Equatable
11+
// for types that require the use of JSONAny, nor will the implementation of Hashable be
12+
// synthesized for types that have collections (such as arrays or dictionaries).
13+
814
import Foundation
915

1016
// MARK: - SettingsStore
11-
struct SettingsStore: Codable {
17+
struct SettingsStore: Codable, Equatable {
1218
var notificationsEnabled, privacyMode: Bool
1319
var theme: Theme
1420
}
1521

16-
enum Theme: String, Codable {
22+
enum Theme: String, Codable, Equatable {
1723
case dark = "dark"
1824
case light = "light"
1925
}

docs/docs/_nav.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
"text": "Docs",
44
"link": "/docs/getting-started/quick-start",
55
"activeMatch": "^/docs/"
6+
},
7+
{
8+
"text": "Brownie (Alpha)",
9+
"link": "/brownie/overview",
10+
"activeMatch": "^/brownie/"
611
}
712
]

docs/docs/brownie/_meta.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"type": "file",
4+
"name": "overview",
5+
"label": "Overview"
6+
},
7+
{
8+
"type": "file",
9+
"name": "store-definition",
10+
"label": "Defining Stores"
11+
},
12+
{
13+
"type": "file",
14+
"name": "codegen",
15+
"label": "Code Generation"
16+
},
17+
{
18+
"type": "file",
19+
"name": "typescript-usage",
20+
"label": "TypeScript Usage"
21+
},
22+
{
23+
"type": "file",
24+
"name": "swift-usage",
25+
"label": "Swift Usage"
26+
}
27+
]

docs/docs/brownie/codegen.mdx

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Code Generation
2+
3+
The Brownie CLI generates native types from your TypeScript store definitions.
4+
5+
## Usage
6+
7+
```bash
8+
brownie codegen # Generate for all configured platforms
9+
brownie codegen -p swift # Generate Swift only
10+
brownie codegen --platform kotlin # Generate Kotlin only (coming soon)
11+
brownie --help # Show help
12+
brownie --version # Show version
13+
```
14+
15+
## Configuration
16+
17+
Add to your app's `package.json`:
18+
19+
```json
20+
{
21+
"brownie": {
22+
"swift": "./ios/Generated/",
23+
"kotlin": "./android/app/src/main/java/com/example/",
24+
"kotlinPackageName": "com.example"
25+
}
26+
}
27+
```
28+
29+
| Field | Required | Description |
30+
| ------------------- | -------- | ---------------------------------------------------------- |
31+
| `swift` | No\* | Output directory for Swift files |
32+
| `kotlin` | No\* | Output directory for Kotlin files |
33+
| `kotlinPackageName` | No | Kotlin package name (extracted from path if not specified) |
34+
35+
\*At least one of `swift` or `kotlin` is required.
36+
37+
## Generated Output
38+
39+
For this TypeScript definition:
40+
41+
```ts
42+
interface BrownfieldStore extends BrownieStore {
43+
counter: number;
44+
user: { name: string };
45+
}
46+
```
47+
48+
### Swift Output
49+
50+
```swift
51+
import Brownie
52+
53+
struct BrownfieldStore: Codable {
54+
var counter: Double
55+
var user: User
56+
}
57+
58+
struct User: Codable {
59+
var name: String
60+
}
61+
62+
extension BrownfieldStore: BrownieStoreProtocol {
63+
public static let storeName = "BrownfieldStore"
64+
}
65+
```
66+
67+
The generated struct:
68+
69+
- Conforms to `Codable` for JSON serialization
70+
- Conforms to `BrownieStoreProtocol` with auto-generated `storeName`
71+
- Uses mutable `var` properties
72+
73+
### Kotlin Output (Coming Soon)
74+
75+
```kotlin
76+
package com.example
77+
78+
data class BrownfieldStore(
79+
val counter: Double,
80+
val user: User
81+
)
82+
83+
data class User(
84+
val name: String
85+
)
86+
```
87+
88+
## Auto-Generation Hooks
89+
90+
### iOS (Podfile)
91+
92+
Run codegen before pod install:
93+
94+
```ruby
95+
pre_install do |installer|
96+
system("npx", "brownie", "codegen", "-p", "swift")
97+
end
98+
```
99+
100+
### Android (build.gradle.kts)
101+
102+
Run codegen before build:
103+
104+
```kotlin
105+
tasks.register("generateBrownfieldStore") {
106+
exec { commandLine("npx", "brownie", "codegen", "-p", "kotlin") }
107+
}
108+
preBuild.dependsOn("generateBrownfieldStore")
109+
```
110+
111+
## How It Works
112+
113+
1. CLI recursively finds all `*.brownie.ts` files
114+
2. Parses `declare module '@callstack/brownie'` blocks using ts-morph
115+
3. Extracts store names from `BrownieStores` interface
116+
4. Generates native types using quicktype

0 commit comments

Comments
 (0)