Skip to content

Commit 4b619f5

Browse files
committed
feat: docs
1 parent 983c086 commit 4b619f5

9 files changed

Lines changed: 75 additions & 87 deletions

File tree

apps/AppleApp/Brownfield Apple App/ContentView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ let initialState = BrownfieldStore(
77
user: User(name: "Username")
88
)
99

10-
1110
struct ContentView: View {
1211
var body: some View {
1312
NavigationView {

apps/RNApp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"build:example:ios-rn": "react-native build-ios",
1010
"brownfield:package:android": "brownfield package:android --module-name :BrownfieldLib --variant release --verbose",
1111
"brownfield:publish:android": "brownfield publish:android --module-name :BrownfieldLib --verbose",
12-
"brownfield:package:ios": "brownfield package:ios --scheme BrownfieldLib",
12+
"brownfield:package:ios": "brownfield package:ios --scheme BrownfieldLib --configuration Release --verbose",
1313
"lint": "eslint .",
1414
"start": "react-native start",
1515
"test": "jest",

docs/docs/brownie/codegen.mdx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ Add to your app's `package.json`:
1919
```json
2020
{
2121
"brownie": {
22-
"swift": "./ios/Generated/",
2322
"kotlin": "./android/app/src/main/java/com/example/",
2423
"kotlinPackageName": "com.example"
2524
}
2625
}
2726
```
2827

28+
Note: This config is subject to change as Android support is rolled out. Ideally this will be removed in favor of auto-detection.
29+
2930
| Field | Required | Description |
3031
| ------------------- | -------- | ---------------------------------------------------------- |
31-
| `swift` | No\* | Output directory for Swift files |
32-
| `kotlin` | No\* | Output directory for Kotlin files |
32+
| `kotlin` | No | Output directory for Kotlin files |
3333
| `kotlinPackageName` | No | Kotlin package name (extracted from path if not specified) |
3434

35-
\*At least one of `swift` or `kotlin` is required.
35+
**Note:** Swift files are always generated to `node_modules/@callstack/brownie/ios/Generated/`. This path is auto-resolved and not configurable.
3636

3737
## Generated Output
3838

@@ -47,16 +47,18 @@ interface BrownfieldStore extends BrownieStore {
4747

4848
### Swift Output
4949

50+
Generated to: `node_modules/@callstack/brownie/ios/Generated/BrownfieldStore.swift`
51+
5052
```swift
5153
import Brownie
5254

53-
struct BrownfieldStore: Codable {
54-
var counter: Double
55-
var user: User
55+
public struct BrownfieldStore: Codable {
56+
public var counter: Double
57+
public var user: User
5658
}
5759

58-
struct User: Codable {
59-
var name: String
60+
public struct User: Codable {
61+
public var name: String
6062
}
6163

6264
extension BrownfieldStore: BrownieStoreProtocol {
@@ -66,6 +68,7 @@ extension BrownfieldStore: BrownieStoreProtocol {
6668

6769
The generated struct:
6870

71+
- Uses `public` access level for framework compatibility
6972
- Conforms to `Codable` for JSON serialization
7073
- Conforms to `BrownieStoreProtocol` with auto-generated `storeName`
7174
- Uses mutable `var` properties

docs/docs/brownie/overview.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,13 @@ function Counter() {
9898
import Brownie
9999

100100
struct ContentView: View {
101-
@UseStore<AppStore> var store
101+
@UseStore(\AppStore.counter) var counter
102102

103103
var body: some View {
104104
VStack {
105-
Text("Count: \(Int(store.state.counter))")
105+
Text("Count: \(Int(counter))")
106106
Button("Increment") {
107-
store.set { $0.counter += 1 }
107+
$counter.set { $0 + 1 }
108108
}
109109
}
110110
}

docs/docs/brownie/store-definition.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ interface BrownfieldStore extends BrownieStore {
4343
}
4444
```
4545

46-
Generated Swift:
46+
Generated Swift (to `node_modules/@callstack/brownie/ios/Generated/`):
4747

4848
```swift
49-
struct BrownfieldStore: Codable {
50-
var counter: Double
51-
var user: User
49+
public struct BrownfieldStore: Codable {
50+
public var counter: Double
51+
public var user: User
5252
}
5353

54-
struct User: Codable {
55-
var name: String
56-
var email: String
54+
public struct User: Codable {
55+
public var name: String
56+
public var email: String
5757
}
5858
```
5959

packages/brownie/ArchitectureOverview.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ Add to your app's `package.json`:
4848
```json
4949
{
5050
"brownie": {
51-
"swift": "./ios/Generated/",
5251
"kotlin": "./android/app/src/main/java/com/example/",
5352
"kotlinPackageName": "com.example"
5453
}
@@ -57,11 +56,10 @@ Add to your app's `package.json`:
5756

5857
| Field | Required | Description |
5958
| ------------------- | -------- | ----------------------------------------- |
60-
| `swift` | No\* | Output directory for Swift files |
61-
| `kotlin` | No\* | Output directory for Kotlin files |
59+
| `kotlin` | No | Output directory for Kotlin files |
6260
| `kotlinPackageName` | No | Kotlin package name (extracted from path) |
6361

64-
\*At least one of `swift` or `kotlin` is required.
62+
**Note:** Swift files are always generated to `node_modules/@callstack/brownie/ios/Generated/`. This path is auto-resolved and not configurable.
6563

6664
### Store Definition
6765

@@ -110,16 +108,18 @@ declare module '@callstack/brownie' {
110108

111109
### Generated Output
112110

113-
**Swift** (`Codable` struct with mutable properties):
111+
**Swift** (`Codable` struct with public access and mutable properties):
114112

115113
```swift
116-
struct BrownfieldStore: Codable {
117-
var counter: Double
118-
var isLoading: Bool
119-
var user: String
114+
public struct BrownfieldStore: Codable {
115+
public var counter: Double
116+
public var isLoading: Bool
117+
public var user: String
120118
}
121119
```
122120

121+
Generated to: `node_modules/@callstack/brownie/ios/Generated/BrownfieldStore.swift`
122+
123123
**Kotlin** (data class):
124124

125125
```kotlin
@@ -185,7 +185,9 @@ packages/brownie/
185185
├── ios/
186186
│ ├── BrownieModule.h/.mm # TurboModule, installs JSI bindings
187187
│ ├── BrownieStoreBridge.h/.mm # ObjC++ wrapper for Swift access
188-
│ └── BrownieStore.swift # Swift Store<T>, StoreManager, @UseStore
188+
│ ├── BrownieFollyConvert.h # FollyConvert import compatibility header
189+
│ ├── BrownieStore.swift # Swift Store<T>, StoreManager, @UseStore
190+
│ └── Generated/ # Auto-generated Swift store types (codegen output)
189191
```
190192

191193
### C++ Core
@@ -216,6 +218,13 @@ packages/brownie/
216218

217219
### iOS Bridge
218220

221+
**BrownieFollyConvert.h** - Compatibility header for FollyConvert imports:
222+
223+
- Handles different import paths depending on CocoaPods configuration
224+
- Supports static libs with header maps (default)
225+
- Supports `use_frameworks! :linkage => :static` setups
226+
- Falls back to `RCTFollyConvert.h` when available
227+
219228
**BrownieStoreBridge** (ObjC++) - Exposes C++ to Swift:
220229

221230
- `registerStore(withKey:)` - Create C++ store, set up notification callback

packages/cli/src/brownfield/commands/packageIos.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515

1616
import { Command } from 'commander';
1717

18-
import { hasBrownieConfig } from '../../brownie/config.js';
18+
import { isBrownieInstalled } from '../../brownie/config.js';
1919
import { runCodegen } from '../../brownie/commands/codegen.js';
2020
import { getProjectInfo } from '../utils/index.js';
2121
import {
@@ -73,7 +73,7 @@ export const packageIosCommand = curryOptions(
7373
platformConfig
7474
);
7575

76-
if (hasBrownieConfig(projectRoot)) {
76+
if (isBrownieInstalled(projectRoot)) {
7777
await runCodegen({ platform: 'swift' });
7878

7979
const productsPath = path.join(options.buildFolder, 'Build', 'Products');

packages/cli/src/brownie/__tests__/config.test.ts

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'node:path';
55
import { afterEach, describe, expect, it, vi } from 'vitest';
66

77
import {
8-
hasBrownieConfig,
8+
isBrownieInstalled,
99
loadConfig,
1010
getBrowniePackagePath,
1111
getSwiftOutputPath,
@@ -76,39 +76,14 @@ describe('loadConfig', () => {
7676
});
7777
});
7878

79-
describe('hasBrownieConfig', () => {
80-
let tempDir: string | null = null;
81-
82-
afterEach(() => {
83-
if (tempDir) {
84-
cleanupTempDir(tempDir);
85-
tempDir = null;
86-
}
87-
});
88-
89-
it('returns false when package.json not found', () => {
90-
expect(hasBrownieConfig('/nonexistent/path')).toBe(false);
91-
});
92-
93-
it('returns false when brownie config missing', () => {
94-
tempDir = createTempPackageJson({});
95-
expect(hasBrownieConfig(tempDir)).toBe(false);
79+
describe('isBrownieInstalled', () => {
80+
it('returns false when brownie is not installed', () => {
81+
expect(isBrownieInstalled('/nonexistent/path')).toBe(false);
9682
});
9783

98-
it('returns true when brownie config exists', () => {
99-
tempDir = createTempPackageJson({
100-
brownie: {},
101-
});
102-
expect(hasBrownieConfig(tempDir)).toBe(true);
103-
});
104-
105-
it('returns true when brownie config has kotlin', () => {
106-
tempDir = createTempPackageJson({
107-
brownie: {
108-
kotlin: './Generated',
109-
},
110-
});
111-
expect(hasBrownieConfig(tempDir)).toBe(true);
84+
it('returns true when brownie is installed', () => {
85+
const rnAppPath = path.resolve(__dirname, '../../../../../apps/RNApp');
86+
expect(isBrownieInstalled(rnAppPath)).toBe(true);
11287
});
11388
});
11489

packages/cli/src/brownie/config.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,37 @@ interface PackageJson {
1111
brownie?: BrownieConfig;
1212
}
1313

14+
/**
15+
* Checks if @callstack/brownie package is installed.
16+
*/
17+
export function isBrownieInstalled(
18+
projectRoot: string = process.cwd()
19+
): boolean {
20+
const require = createRequire(path.join(projectRoot, 'package.json'));
21+
try {
22+
require.resolve('@callstack/brownie/package.json');
23+
return true;
24+
} catch {
25+
return false;
26+
}
27+
}
28+
1429
/**
1530
* Resolves the path to the @callstack/brownie package.
1631
*/
1732
export function getBrowniePackagePath(
1833
projectRoot: string = process.cwd()
1934
): string {
2035
const require = createRequire(path.join(projectRoot, 'package.json'));
21-
const browniePackageJson = require.resolve('@callstack/brownie/package.json');
22-
return path.dirname(browniePackageJson);
36+
try {
37+
const browniePackageJson =
38+
require.resolve('@callstack/brownie/package.json');
39+
return path.dirname(browniePackageJson);
40+
} catch {
41+
throw new Error(
42+
"@callstack/brownie is not installed. Run 'npm install @callstack/brownie' or 'yarn add @callstack/brownie'"
43+
);
44+
}
2345
}
2446

2547
/**
@@ -32,26 +54,6 @@ export function getSwiftOutputPath(
3254
return path.join(browniePath, 'ios', 'Generated');
3355
}
3456

35-
/**
36-
* Checks if brownie config exists in package.json.
37-
*/
38-
export function hasBrownieConfig(projectRoot: string = process.cwd()): boolean {
39-
const packageJsonPath = path.resolve(projectRoot, 'package.json');
40-
41-
if (!fs.existsSync(packageJsonPath)) {
42-
return false;
43-
}
44-
45-
try {
46-
const packageJson: PackageJson = JSON.parse(
47-
fs.readFileSync(packageJsonPath, 'utf-8')
48-
);
49-
return packageJson.brownie !== undefined;
50-
} catch {
51-
return false;
52-
}
53-
}
54-
5557
/**
5658
* Loads brownie config from package.json in the current working directory.
5759
*/

0 commit comments

Comments
 (0)