@swiftbrowser/native 0.26.2 → 0.26.4
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.
- package/README.md +7 -7
- package/package.json +3 -3
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeApp.swift +6 -3
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/KeyboardBar.swift +19 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Lists.swift +3 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Navigation.swift +1 -0
- package/swift/Sources/SwiftBrowserNative/Views/NativeRootView.swift +11 -1
- package/swift/Sources/SwiftBrowserProtocol/Props.swift +6 -0
- package/swift/Sources/SwiftBrowserProtocol/RendererEvent.swift +5 -2
- package/wasm/SwiftBrowserNative.wasm +2 -2
package/README.md
CHANGED
|
@@ -37,10 +37,10 @@ build against the exact version of the renderer this JavaScript was written
|
|
|
37
37
|
for. `swiftbrowser native init` lays a project out that way:
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
|
-
npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj,
|
|
40
|
+
npx @swiftbrowser/cli native init MyApp # MyApp.xcodeproj (iOS and macOS), apple/, src/, web/
|
|
41
41
|
cd MyApp && npm install # React, @swiftbrowser/react, @swiftbrowser/web and this package
|
|
42
|
-
npm run build #
|
|
43
|
-
open MyApp.xcodeproj # run it on a simulator
|
|
42
|
+
npm run build # apple/app.js, the bundle the app ships
|
|
43
|
+
open MyApp.xcodeproj # run it on a simulator, an iPhone or this Mac
|
|
44
44
|
npm run dev:web # the same app as a web page
|
|
45
45
|
```
|
|
46
46
|
|
|
@@ -76,8 +76,8 @@ run(<App />);
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
```sh
|
|
79
|
-
npx swiftbrowser-native build src/index.tsx --out ../
|
|
80
|
-
npx swiftbrowser-native dev src/index.tsx --out ../
|
|
79
|
+
npx swiftbrowser-native build src/index.tsx --out ../apple/app.js # the bundle Xcode ships
|
|
80
|
+
npx swiftbrowser-native dev src/index.tsx --out ../apple/app.js # http://localhost:5173/, Fast Refresh on save
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
On the Swift side, `NativeApp().load()` runs the bundled `app.js` and
|
|
@@ -152,7 +152,7 @@ the app's scene phase here (`active`, `inactive`, `background`), as
|
|
|
152
152
|
## Workers
|
|
153
153
|
|
|
154
154
|
```sh
|
|
155
|
-
npx swiftbrowser-native build src/index.tsx --out ../
|
|
155
|
+
npx swiftbrowser-native build src/index.tsx --out ../apple/app.js --worker render=src/render.worker.ts
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
```ts
|
|
@@ -410,7 +410,7 @@ and a page can register its own element under the same name
|
|
|
410
410
|
also from @swiftbrowser/react), so the same element draws on both.
|
|
411
411
|
|
|
412
412
|
Swift that is not a view registers as a function, before the bundle loads
|
|
413
|
-
(`swiftbrowser native init` writes an `
|
|
413
|
+
(`swiftbrowser native init` writes an `apple/NativeFunctions.swift` for them):
|
|
414
414
|
|
|
415
415
|
```swift
|
|
416
416
|
NativeFunctions.register("purchase") { call in // async
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swiftbrowser/native",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Run a @swiftbrowser/react app natively: the runtime a JavaScriptCore-hosted app needs (timers, console, fetch and the other web globals over the SwiftBrowser Native bridge), the `run` entry that renders into the native SwiftUI renderer, and the bundler that builds app.js for the Xcode target.",
|
|
6
6
|
"repository": {
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@babel/core": "^7.29.7",
|
|
66
|
-
"@swiftbrowser/react": "0.26.
|
|
67
|
-
"@swiftbrowser/web": "0.26.
|
|
66
|
+
"@swiftbrowser/react": "0.26.4",
|
|
67
|
+
"@swiftbrowser/web": "0.26.4",
|
|
68
68
|
"esbuild": "^0.28.2",
|
|
69
69
|
"react-refresh": "^0.17.0"
|
|
70
70
|
},
|
|
@@ -37,6 +37,8 @@ public final class NativeApp {
|
|
|
37
37
|
public private(set) var colorScheme: String?
|
|
38
38
|
public private(set) var dynamicTypeSize: String?
|
|
39
39
|
public private(set) var horizontalSizeClass: String?
|
|
40
|
+
/// The platform the app runs on, `ios` or `macos` (docs/notes/macos.md), once the root view has said.
|
|
41
|
+
public private(set) var platform: String?
|
|
40
42
|
/// URLs the app was opened with before its script could take them (a
|
|
41
43
|
/// universal link that launched it): delivered once it loads.
|
|
42
44
|
private var pendingURLs: [URL] = []
|
|
@@ -312,17 +314,18 @@ public final class NativeApp {
|
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
/// Tells the app the device environment; sent once at load and on change.
|
|
315
|
-
public func setEnvironment(colorScheme: String? = nil, dynamicTypeSize: String? = nil, horizontalSizeClass: String? = nil) {
|
|
317
|
+
public func setEnvironment(colorScheme: String? = nil, dynamicTypeSize: String? = nil, horizontalSizeClass: String? = nil, platform: String? = nil) {
|
|
316
318
|
var changed = false
|
|
317
319
|
if let colorScheme, colorScheme != self.colorScheme { self.colorScheme = colorScheme; changed = true }
|
|
318
320
|
if let dynamicTypeSize, dynamicTypeSize != self.dynamicTypeSize { self.dynamicTypeSize = dynamicTypeSize; changed = true }
|
|
319
321
|
if let horizontalSizeClass, horizontalSizeClass != self.horizontalSizeClass { self.horizontalSizeClass = horizontalSizeClass; changed = true }
|
|
322
|
+
if let platform, platform != self.platform { self.platform = platform; changed = true }
|
|
320
323
|
if changed { sendEnvironment() }
|
|
321
324
|
}
|
|
322
325
|
|
|
323
326
|
private func sendEnvironment() {
|
|
324
|
-
guard isLoaded, colorScheme != nil || dynamicTypeSize != nil || horizontalSizeClass != nil else { return }
|
|
325
|
-
dispatch(.environment(colorScheme: colorScheme, dynamicTypeSize: dynamicTypeSize, horizontalSizeClass: horizontalSizeClass))
|
|
327
|
+
guard isLoaded, colorScheme != nil || dynamicTypeSize != nil || horizontalSizeClass != nil || platform != nil else { return }
|
|
328
|
+
dispatch(.environment(colorScheme: colorScheme, dynamicTypeSize: dynamicTypeSize, horizontalSizeClass: horizontalSizeClass, platform: platform))
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
/// The scene moved between the foreground, inactive and the background.
|
|
@@ -81,4 +81,23 @@ private struct CapsuleGlass: ViewModifier {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
#endif
|
|
84
|
+
|
|
85
|
+
/// `.navigationSubtitle` (macOS 11, iOS 26): the screen's `subtitle`, under
|
|
86
|
+
/// the title in a Mac's title bar. The iOS build's SDK floor is older than
|
|
87
|
+
/// the modifier, so only a Mac (and the shim's round trip) draws it.
|
|
88
|
+
struct NavigationSubtitle: ViewModifier {
|
|
89
|
+
let subtitle: String?
|
|
90
|
+
|
|
91
|
+
func body(content: Content) -> some View {
|
|
92
|
+
#if os(macOS) || canImport(FoundationBridge)
|
|
93
|
+
if let subtitle, !subtitle.isEmpty {
|
|
94
|
+
content.navigationSubtitle(subtitle)
|
|
95
|
+
} else {
|
|
96
|
+
content
|
|
97
|
+
}
|
|
98
|
+
#else
|
|
99
|
+
content
|
|
100
|
+
#endif
|
|
101
|
+
}
|
|
102
|
+
}
|
|
84
103
|
#endif
|
|
@@ -15,6 +15,9 @@ struct ListElement: View {
|
|
|
15
15
|
let props = ListProps(props: node.props)
|
|
16
16
|
let list = selectionList(props)
|
|
17
17
|
let styled: AnyView = {
|
|
18
|
+
// A `Form` (the shim's and React's mark their list `form`): the
|
|
19
|
+
// system's form, which on a Mac is the columns style.
|
|
20
|
+
if props.form { return AnyView(Form { ListRowsView(children: node.children) }) }
|
|
18
21
|
switch props.style {
|
|
19
22
|
case "plain": return AnyView(list.listStyle(.plain))
|
|
20
23
|
case "sidebar": return AnyView(list.listStyle(.sidebar))
|
|
@@ -145,6 +145,7 @@ struct NavigationScreenElement: View {
|
|
|
145
145
|
let titled = content
|
|
146
146
|
.modifier(KeyboardBarInset(toolbars: keyboardToolbars))
|
|
147
147
|
.navigationTitle(props.title)
|
|
148
|
+
.modifier(NavigationSubtitle(subtitle: props.subtitle))
|
|
148
149
|
.toolbar { ScreenToolbar(toolbars: shown) }
|
|
149
150
|
.onAppear {
|
|
150
151
|
// A pushed screen, appearing: its bar as it is now, until the push has settled.
|
|
@@ -84,9 +84,19 @@ public struct NativeRootView: View {
|
|
|
84
84
|
app.setEnvironment(
|
|
85
85
|
colorScheme: colorScheme == .dark ? "dark" : "light",
|
|
86
86
|
dynamicTypeSize: dynamicTypeSize.protocolName,
|
|
87
|
-
horizontalSizeClass: sizeClassName
|
|
87
|
+
horizontalSizeClass: sizeClassName,
|
|
88
|
+
platform: Self.platformName
|
|
88
89
|
)
|
|
89
90
|
}
|
|
91
|
+
|
|
92
|
+
/// The platform the app is drawn on (docs/notes/macos.md): the OS, as the protocol names it.
|
|
93
|
+
static var platformName: String {
|
|
94
|
+
#if os(macOS)
|
|
95
|
+
"macos"
|
|
96
|
+
#else
|
|
97
|
+
"ios"
|
|
98
|
+
#endif
|
|
99
|
+
}
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
/// Presents the first of `presented` over `content` (a sheet, a full-screen
|
|
@@ -175,11 +175,14 @@ public struct ListProps: Equatable, Sendable {
|
|
|
175
175
|
public var editing: Bool?
|
|
176
176
|
/// `List(selection:)` over a `Set`: several rows may be selected.
|
|
177
177
|
public var multiSelect: Bool
|
|
178
|
+
/// A `Form`: drawn as one (on a Mac, the columns style).
|
|
179
|
+
public var form: Bool
|
|
178
180
|
public init(props: JSONObject) {
|
|
179
181
|
style = props.string("style") ?? "insetGrouped"
|
|
180
182
|
refreshable = props.bool("refreshable") ?? false
|
|
181
183
|
editing = props.bool("editing")
|
|
182
184
|
multiSelect = props.bool("multiSelect") ?? false
|
|
185
|
+
form = props.bool("form") ?? false
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
|
|
@@ -294,6 +297,8 @@ public struct ToggleProps: Equatable, Sendable {
|
|
|
294
297
|
|
|
295
298
|
public struct NavScreenProps: Equatable, Sendable {
|
|
296
299
|
public var title: String
|
|
300
|
+
/// `.navigationSubtitle`: under the title in a Mac's title bar.
|
|
301
|
+
public var subtitle: String?
|
|
297
302
|
/// `automatic`, `large` or `inline`.
|
|
298
303
|
public var displayMode: String
|
|
299
304
|
/// 0 for the stack's root screen.
|
|
@@ -306,6 +311,7 @@ public struct NavScreenProps: Equatable, Sendable {
|
|
|
306
311
|
public var statusBarHidden: Bool
|
|
307
312
|
public init(props: JSONObject) {
|
|
308
313
|
title = props.string("title") ?? ""
|
|
314
|
+
subtitle = props.string("subtitle")
|
|
309
315
|
displayMode = props.string("displayMode") ?? "automatic"
|
|
310
316
|
depth = props.int("depth") ?? 0
|
|
311
317
|
toolbarRole = props.string("toolbarRole")
|
|
@@ -163,18 +163,21 @@ public struct RendererEvent: Equatable, Sendable {
|
|
|
163
163
|
with("registered", id: id, ["name": .string(name), "value": value])
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
/// The device appearance, text size
|
|
166
|
+
/// The device appearance, text size, size class or platform (`ios` or
|
|
167
|
+
/// `macos`, docs/notes/macos.md; sent once at launch and on change).
|
|
167
168
|
public static func environment(
|
|
168
169
|
colorScheme: String? = nil,
|
|
169
170
|
dynamicTypeSize: String? = nil,
|
|
170
171
|
horizontalSizeClass: String? = nil,
|
|
171
|
-
scenePhase: String? = nil
|
|
172
|
+
scenePhase: String? = nil,
|
|
173
|
+
platform: String? = nil
|
|
172
174
|
) -> RendererEvent {
|
|
173
175
|
var fields: JSONObject = [:]
|
|
174
176
|
if let colorScheme { fields["colorScheme"] = .string(colorScheme) }
|
|
175
177
|
if let dynamicTypeSize { fields["dynamicTypeSize"] = .string(dynamicTypeSize) }
|
|
176
178
|
if let horizontalSizeClass { fields["horizontalSizeClass"] = .string(horizontalSizeClass) }
|
|
177
179
|
if let scenePhase { fields["scenePhase"] = .string(scenePhase) }
|
|
180
|
+
if let platform { fields["platform"] = .string(platform) }
|
|
178
181
|
return RendererEvent(type: "environment", fields: fields)
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/wasm/SwiftBrowserNative.wasm
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 50287835 bytes
|
|
4
|
+
sha256: af44f347a82278412c3e963bbb3d47e240c12ec80a8220b5641277010d238984
|