@swiftbrowser/swift 0.0.0-stage → 0.1.0

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.
Files changed (74) hide show
  1. package/Package.swift +23 -0
  2. package/README.md +12 -2
  3. package/Sources/Charts/Charts.swift +65 -0
  4. package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
  5. package/Sources/FoundationBridge/Locale.swift +86 -0
  6. package/Sources/FoundationBridge/Timer.swift +48 -0
  7. package/Sources/FoundationBridge/UITextChecker.swift +30 -0
  8. package/Sources/FoundationLite/Common.swift +3 -0
  9. package/Sources/FoundationLite/FoundationLite.swift +171 -0
  10. package/Sources/FoundationLite/Interaction.swift +146 -0
  11. package/Sources/FoundationLite/Timer.swift +48 -0
  12. package/Sources/SwiftUI/API/Accessibility.swift +132 -0
  13. package/Sources/SwiftUI/API/Alert.swift +221 -0
  14. package/Sources/SwiftUI/API/Animation.swift +253 -0
  15. package/Sources/SwiftUI/API/App.swift +57 -0
  16. package/Sources/SwiftUI/API/Color.swift +161 -0
  17. package/Sources/SwiftUI/API/Controls.swift +268 -0
  18. package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
  19. package/Sources/SwiftUI/API/Decorations.swift +92 -0
  20. package/Sources/SwiftUI/API/EditMode.swift +269 -0
  21. package/Sources/SwiftUI/API/Effects.swift +290 -0
  22. package/Sources/SwiftUI/API/Environment.swift +355 -0
  23. package/Sources/SwiftUI/API/Focus.swift +129 -0
  24. package/Sources/SwiftUI/API/Font.swift +131 -0
  25. package/Sources/SwiftUI/API/Gauge.swift +289 -0
  26. package/Sources/SwiftUI/API/Geometry.swift +89 -0
  27. package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
  28. package/Sources/SwiftUI/API/Gestures.swift +342 -0
  29. package/Sources/SwiftUI/API/Grid.swift +188 -0
  30. package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
  31. package/Sources/SwiftUI/API/Layout.swift +165 -0
  32. package/Sources/SwiftUI/API/Lists.swift +276 -0
  33. package/Sources/SwiftUI/API/Menu.swift +51 -0
  34. package/Sources/SwiftUI/API/Modifiers.swift +243 -0
  35. package/Sources/SwiftUI/API/Navigation.swift +371 -0
  36. package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
  37. package/Sources/SwiftUI/API/OnChange.swift +48 -0
  38. package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
  39. package/Sources/SwiftUI/API/Picker.swift +176 -0
  40. package/Sources/SwiftUI/API/Presentation.swift +109 -0
  41. package/Sources/SwiftUI/API/Primitives.swift +390 -0
  42. package/Sources/SwiftUI/API/Publishers.swift +107 -0
  43. package/Sources/SwiftUI/API/RowActions.swift +83 -0
  44. package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
  45. package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
  46. package/Sources/SwiftUI/API/State.swift +127 -0
  47. package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
  48. package/Sources/SwiftUI/API/Tabs.swift +119 -0
  49. package/Sources/SwiftUI/API/Tasks.swift +35 -0
  50. package/Sources/SwiftUI/API/Timeline.swift +212 -0
  51. package/Sources/SwiftUI/API/Toolbar.swift +215 -0
  52. package/Sources/SwiftUI/API/View.swift +207 -0
  53. package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
  54. package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
  55. package/Sources/SwiftUI/Charts/Axis.swift +319 -0
  56. package/Sources/SwiftUI/Charts/Chart.swift +646 -0
  57. package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
  58. package/Sources/SwiftUI/Charts/Marks.swift +204 -0
  59. package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
  60. package/Sources/SwiftUI/Charts/Scales.swift +326 -0
  61. package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
  62. package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
  63. package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
  64. package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
  65. package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
  66. package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
  67. package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
  68. package/Sources/_FoundationCommon/Bundle.swift +82 -0
  69. package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
  70. package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
  71. package/index.d.ts +2 -0
  72. package/index.mjs +16 -0
  73. package/package.json +28 -4
  74. package/sync-sources.mjs +19 -0
@@ -0,0 +1,279 @@
1
+ // Combine-style observation: `ObservableObject`, `@Published`, `@StateObject`,
2
+ // `@ObservedObject` and `@EnvironmentObject`, with the public signatures user
3
+ // code expects from SwiftUI and Combine, minus Combine itself.
4
+ //
5
+ // The runtime re-renders the whole tree when it is invalidated, so a publisher
6
+ // does not need subscribers in the Combine sense: it only has to know how to
7
+ // invalidate the runtime once a mounted view observes its object.
8
+
9
+ // MARK: - ObservableObject
10
+
11
+ /// A type whose changes a view can observe.
12
+ ///
13
+ /// Combine declares `objectWillChange` with an associated publisher type; this
14
+ /// shim uses the concrete `ObservableObjectPublisher`, which is what the
15
+ /// default implementation produces on Apple's platforms as well, so a class
16
+ /// that only writes `class Model: ObservableObject` compiles unchanged.
17
+ ///
18
+ /// The protocol is deliberately not actor-isolated, so both a plain
19
+ /// `final class` and a `@MainActor class` can conform.
20
+ public protocol ObservableObject: AnyObject {
21
+ var objectWillChange: ObservableObjectPublisher { get }
22
+ }
23
+
24
+ extension ObservableObject {
25
+ /// The object's publisher, created on first access and kept for as long
26
+ /// as the object lives (see `ObservableObjectPublisher._publisher(for:)`).
27
+ public var objectWillChange: ObservableObjectPublisher {
28
+ ObservableObjectPublisher._publisher(for: self)
29
+ }
30
+ }
31
+
32
+ /// Publishes before an `ObservableObject` changes.
33
+ ///
34
+ /// `send()` invalidates the runtime when some mounted view observes the
35
+ /// object. All view code and every `@Published` mutation in this shim run on
36
+ /// the main actor, which is where the runtime invalidation happens.
37
+ public final class ObservableObjectPublisher {
38
+ /// Installed by the runtime through the property wrappers: one entry per
39
+ /// observing mounted view (or `StateObject` storage), so a change marks
40
+ /// every view that observes the object. Entries whose owner is gone are
41
+ /// dropped on the next `send()`.
42
+ private var subscribers: [ObjectIdentifier: (owner: _WeakOwner, onChange: () -> Void)] = [:]
43
+ /// The object this publisher belongs to; used to detect a recycled
44
+ /// `ObjectIdentifier`.
45
+ weak var _owner: AnyObject?
46
+
47
+ /// Publishers keyed by the identity of their object. The protocol's
48
+ /// default implementation has no per-object storage to put the publisher
49
+ /// in, so the shim keeps it here. A publisher outlives its object until a
50
+ /// new object reuses the address, which replaces the stale entry; the leak
51
+ /// is one small object per `ObservableObject` instance. The registry is
52
+ /// unsynchronized because the shim runs single-threaded on the main actor.
53
+ nonisolated(unsafe) private static var registry: [ObjectIdentifier: ObservableObjectPublisher] = [:]
54
+
55
+ public init() {}
56
+
57
+ /// Tells observing views that the object is about to change.
58
+ public func send() {
59
+ for (key, entry) in subscribers {
60
+ if entry.owner.object == nil {
61
+ subscribers[key] = nil
62
+ } else {
63
+ entry.onChange()
64
+ }
65
+ }
66
+ }
67
+
68
+ /// Registers `onChange` to run on `send()` for as long as `owner` (the
69
+ /// observing view's mounted node or state storage) is alive; a second
70
+ /// subscription by the same owner replaces the first.
71
+ func _subscribe(owner: AnyObject, _ onChange: @escaping () -> Void) {
72
+ subscribers[ObjectIdentifier(owner)] = (_WeakOwner(owner), onChange)
73
+ }
74
+
75
+ static func _publisher(for object: AnyObject) -> ObservableObjectPublisher {
76
+ let id = ObjectIdentifier(object)
77
+ if let existing = registry[id], existing._owner === object { return existing }
78
+ let publisher = ObservableObjectPublisher()
79
+ publisher._owner = object
80
+ registry[id] = publisher
81
+ return publisher
82
+ }
83
+ }
84
+
85
+ // MARK: - @Published
86
+
87
+ /// Publishes the enclosing `ObservableObject`'s `objectWillChange` before a
88
+ /// property changes.
89
+ ///
90
+ /// Combine's `Published.Publisher` (`$property`) is not provided.
91
+ @propertyWrapper
92
+ public struct Published<Value> {
93
+ var value: Value
94
+
95
+ public init(wrappedValue: Value) { value = wrappedValue }
96
+ public init(initialValue: Value) { value = initialValue }
97
+
98
+ @available(*, unavailable, message: "@Published is only available on properties of classes")
99
+ public var wrappedValue: Value {
100
+ get { fatalError("@Published is only available on properties of classes") }
101
+ set { fatalError("@Published is only available on properties of classes") }
102
+ }
103
+
104
+ public static subscript<T: AnyObject>(
105
+ _enclosingInstance instance: T,
106
+ wrapped _: ReferenceWritableKeyPath<T, Value>,
107
+ storage storageKeyPath: ReferenceWritableKeyPath<T, Published<Value>>
108
+ ) -> Value {
109
+ get { instance[keyPath: storageKeyPath].value }
110
+ set {
111
+ (instance as? any ObservableObject)?.objectWillChange.send()
112
+ instance[keyPath: storageKeyPath].value = newValue
113
+ }
114
+ }
115
+ }
116
+
117
+ // MARK: - Runtime hooks
118
+
119
+ /// Views expose properties that observe an `ObservableObject` through this
120
+ /// protocol; the runtime finds them by reflection and wires invalidation.
121
+ protocol _ObservingProperty {
122
+ /// Subscribes `onChange` to the observed object on behalf of `owner`
123
+ /// (the mounted view), see `ObservableObjectPublisher._subscribe`.
124
+ func _observe(owner: AnyObject, _ onChange: @escaping () -> Void)
125
+ }
126
+
127
+ /// A weak reference, for the publisher's subscriber table.
128
+ final class _WeakOwner {
129
+ weak var object: AnyObject?
130
+ init(_ object: AnyObject) { self.object = object }
131
+ }
132
+
133
+ // MARK: - @ObservedObject
134
+
135
+ /// Observes an `ObservableObject` owned elsewhere; changes re-render the view.
136
+ @propertyWrapper
137
+ public struct ObservedObject<ObjectType: ObservableObject>: DynamicProperty {
138
+ /// Bindings to the object's properties (`$model.property`).
139
+ @dynamicMemberLookup
140
+ public struct Wrapper {
141
+ let object: ObjectType
142
+
143
+ public subscript<Subject>(dynamicMember keyPath: ReferenceWritableKeyPath<ObjectType, Subject>) -> Binding<Subject> {
144
+ let object = self.object
145
+ return Binding(get: { object[keyPath: keyPath] }, set: { object[keyPath: keyPath] = $0 })
146
+ }
147
+ }
148
+
149
+ public var wrappedValue: ObjectType
150
+
151
+ public init(wrappedValue: ObjectType) { self.wrappedValue = wrappedValue }
152
+ public init(initialValue: ObjectType) { wrappedValue = initialValue }
153
+
154
+ public var projectedValue: Wrapper { Wrapper(object: wrappedValue) }
155
+ }
156
+
157
+ extension ObservedObject: _ObservingProperty {
158
+ func _observe(owner: AnyObject, _ onChange: @escaping () -> Void) {
159
+ wrappedValue.objectWillChange._subscribe(owner: owner, onChange)
160
+ }
161
+ }
162
+
163
+ // MARK: - @StateObject
164
+
165
+ /// The object behind a `StateObject`, created once and shared by every box
166
+ /// for the same mounted view (like `_StateStorage`).
167
+ final class _StateObjectStorage<ObjectType: ObservableObject> {
168
+ private var make: (() -> ObjectType)?
169
+ private(set) var object: ObjectType?
170
+ var onChange: (() -> Void)? {
171
+ didSet { subscribe() }
172
+ }
173
+
174
+ init(_ make: @escaping () -> ObjectType) { self.make = make }
175
+
176
+ /// Creates the object on first access, so a view struct that is
177
+ /// re-created but then adopts a previous render's storage never runs its
178
+ /// initializer expression.
179
+ func resolve() -> ObjectType {
180
+ if let object { return object }
181
+ let created = make!()
182
+ make = nil
183
+ object = created
184
+ subscribe()
185
+ return created
186
+ }
187
+
188
+ private func subscribe() {
189
+ guard let object, let onChange else { return }
190
+ object.objectWillChange._subscribe(owner: self, onChange)
191
+ }
192
+ }
193
+
194
+ final class _StateObjectBox<ObjectType: ObservableObject>: _AnyStateBox {
195
+ var storage: _StateObjectStorage<ObjectType>
196
+
197
+ init(_ make: @escaping () -> ObjectType) { storage = _StateObjectStorage(make) }
198
+
199
+ var _onChange: (() -> Void)? {
200
+ get { storage.onChange }
201
+ set { storage.onChange = newValue }
202
+ }
203
+
204
+ /// The object, or `nil` before it is created. Never a JSON primitive, so
205
+ /// hot-reload snapshots skip it.
206
+ var _anyValue: Any {
207
+ get { storage.object as Any }
208
+ set {}
209
+ }
210
+
211
+ func _adopt(_ previous: any _AnyStateBox) {
212
+ guard let previous = previous as? _StateObjectBox<ObjectType> else { return }
213
+ storage = previous.storage
214
+ }
215
+ }
216
+
217
+ /// Owns an `ObservableObject` for the lifetime of the mounted view.
218
+ ///
219
+ /// The initializer expression runs once, when the view first mounts; the
220
+ /// runtime carries the object across re-renders exactly like `State`.
221
+ @propertyWrapper
222
+ public struct StateObject<ObjectType: ObservableObject>: DynamicProperty {
223
+ let box: _StateObjectBox<ObjectType>
224
+
225
+ public init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType) {
226
+ box = _StateObjectBox(thunk)
227
+ }
228
+
229
+ public var wrappedValue: ObjectType { box.storage.resolve() }
230
+
231
+ public var projectedValue: ObservedObject<ObjectType>.Wrapper {
232
+ ObservedObject<ObjectType>.Wrapper(object: wrappedValue)
233
+ }
234
+ }
235
+
236
+ extension StateObject: _StateProperty {
237
+ var _box: any _AnyStateBox { box }
238
+ }
239
+
240
+ // MARK: - @EnvironmentObject
241
+
242
+ /// Reads an `ObservableObject` an ancestor supplied with `environmentObject(_:)`.
243
+ @propertyWrapper
244
+ public struct EnvironmentObject<ObjectType: ObservableObject>: DynamicProperty {
245
+ /// Bindings to the object's properties (`$model.property`).
246
+ public typealias Wrapper = ObservedObject<ObjectType>.Wrapper
247
+
248
+ let box = _EnvironmentBox()
249
+
250
+ public init() {}
251
+
252
+ public var wrappedValue: ObjectType {
253
+ guard let object = box.value as? ObjectType else {
254
+ fatalError("No ObservableObject of type \(ObjectType.self) found. A View.environmentObject(_:) for \(ObjectType.self) may be missing as an ancestor of this view.")
255
+ }
256
+ return object
257
+ }
258
+
259
+ public var projectedValue: Wrapper { Wrapper(object: wrappedValue) }
260
+ }
261
+
262
+ extension EnvironmentObject: _EnvironmentProperty {
263
+ func _resolve(in environment: EnvironmentValues) {
264
+ box.value = environment._object(ObjectType.self)
265
+ }
266
+ }
267
+
268
+ extension EnvironmentObject: _ObservingProperty {
269
+ func _observe(owner: AnyObject, _ onChange: @escaping () -> Void) {
270
+ (box.value as? ObjectType)?.objectWillChange._subscribe(owner: owner, onChange)
271
+ }
272
+ }
273
+
274
+ extension View {
275
+ /// Supplies `object` to every `EnvironmentObject` of its type in the subtree.
276
+ public func environmentObject<T: ObservableObject>(_ object: T) -> some View {
277
+ _EnvironmentView(content: self) { $0._setObject(object) }
278
+ }
279
+ }
@@ -0,0 +1,48 @@
1
+ /// `.onChange(of:)`: remembers the last value across re-renders (in a `@State`
2
+ /// slot, so it survives like any state) and runs the action when the value
3
+ /// differs. The action runs while the body is evaluated; state it changes
4
+ /// schedules another render pass, which is what SwiftUI does too.
5
+ final class _ChangeSlot<Value> {
6
+ var value: Value?
7
+ var seeded = false
8
+ }
9
+
10
+ struct _OnChangeView<Value: Equatable, Content: View>: View {
11
+ let content: Content
12
+ let value: Value
13
+ let initial: Bool
14
+ let action: (Value, Value) -> Void
15
+ @State private var slot = _ChangeSlot<Value>()
16
+
17
+ var body: some View {
18
+ if !slot.seeded {
19
+ slot.seeded = true
20
+ slot.value = value
21
+ if initial { action(value, value) }
22
+ } else if let previous = slot.value, previous != value {
23
+ slot.value = value
24
+ action(previous, value)
25
+ }
26
+ return content
27
+ }
28
+ }
29
+
30
+ extension View {
31
+ /// The iOS 17 form: `(oldValue, newValue)`.
32
+ public func onChange<V: Equatable>(of value: V, initial: Bool = false,
33
+ _ action: @escaping (_ oldValue: V, _ newValue: V) -> Void) -> some View {
34
+ _OnChangeView(content: self, value: value, initial: initial, action: action)
35
+ }
36
+
37
+ /// The iOS 17 form without parameters.
38
+ public func onChange<V: Equatable>(of value: V, initial: Bool = false,
39
+ _ action: @escaping () -> Void) -> some View {
40
+ _OnChangeView(content: self, value: value, initial: initial) { _, _ in action() }
41
+ }
42
+
43
+ /// The pre-iOS 17 form: `(newValue)`.
44
+ @_disfavoredOverload
45
+ public func onChange<V: Equatable>(of value: V, perform action: @escaping (_ newValue: V) -> Void) -> some View {
46
+ _OnChangeView(content: self, value: value, initial: false) { _, new in action(new) }
47
+ }
48
+ }