Skip to content

Commit a577f66

Browse files
authored
Add ViewTrait and ViewTraitTests (#188)
1 parent 783a0ef commit a577f66

File tree

6 files changed

+378
-114
lines changed

6 files changed

+378
-114
lines changed

Sources/OpenSwiftUICore/View/Trait/ViewTraitCollection.swift

Lines changed: 0 additions & 67 deletions
This file was deleted.

Sources/OpenSwiftUICore/View/Trait/ViewTraitKey.swift

Lines changed: 0 additions & 11 deletions
This file was deleted.

Sources/OpenSwiftUICore/View/Trait/ViewTraitKeys.swift

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
//
2+
// ViewTrait.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for iOS 18.0
6+
// Status: Blocked by ViewList
7+
// ID: 9929B476764059557433A108298EE66F (SwiftUI)
8+
// ID: 48526BA25CDCBF890FA91D018A5421B4 (SwiftUICore)
9+
10+
import OpenGraphShims
11+
12+
// MARK: - ViewTraitKey
13+
14+
/// A type of key for a trait associated with the content of a
15+
/// container view.
16+
public protocol _ViewTraitKey {
17+
/// The type of value produced by the trait.
18+
associatedtype Value
19+
20+
/// The default value of the trait.
21+
static var defaultValue: Value { get }
22+
}
23+
24+
// MARK: - _TraitWritingModifier [TODO]
25+
26+
/// A view content adapter that associates a trait with its base content.
27+
@frozen
28+
public struct _TraitWritingModifier<Trait>: PrimitiveViewModifier where Trait : _ViewTraitKey {
29+
public let value: Trait.Value
30+
31+
@inlinable
32+
public init(value: Trait.Value) {
33+
self.value = value
34+
}
35+
36+
nonisolated public static func _makeView(
37+
modifier: _GraphValue<Self>,
38+
inputs: _ViewInputs,
39+
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
40+
) -> _ViewOutputs {
41+
preconditionFailure("TODO")
42+
}
43+
nonisolated public static func _makeViewList(
44+
modifier: _GraphValue<Self>,
45+
inputs: _ViewListInputs,
46+
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
47+
) -> _ViewListOutputs {
48+
preconditionFailure("TODO")
49+
}
50+
51+
nonisolated public static func _viewListCount(inputs: _ViewListCountInputs, body: (_ViewListCountInputs) -> Int?) -> Int? {
52+
preconditionFailure("TODO")
53+
}
54+
55+
private struct AddTrait {
56+
@Attribute var modifier: _TraitWritingModifier
57+
@OptionalAttribute var traits: ViewTraitCollection?
58+
}
59+
}
60+
61+
@available(*, unavailable)
62+
extension _TraitWritingModifier: Sendable {}
63+
64+
extension View {
65+
/// Associate a trait `value` for the given `key` for this view content.
66+
@inlinable
67+
nonisolated public func _trait<K>(_ key: K.Type, _ value: K.Value) -> some View where K: _ViewTraitKey {
68+
return modifier(_TraitWritingModifier<K>(value: value))
69+
}
70+
}
71+
72+
// MARK: - _ConditionalTraitWritingModifier [TODO]
73+
74+
/// Conditionally writes a trait.
75+
@frozen
76+
public struct _ConditionalTraitWritingModifier<Trait>: PrimitiveViewModifier where Trait : _ViewTraitKey {
77+
public var value: Trait.Value
78+
public var isEnabled: Bool
79+
80+
@_alwaysEmitIntoClient
81+
public init(traitKey: Trait.Type = Trait.self, value: Trait.Value, isEnabled: Bool) {
82+
self.value = value
83+
self.isEnabled = isEnabled
84+
}
85+
86+
nonisolated public static func _makeView(
87+
modifier: _GraphValue<Self>,
88+
inputs: _ViewInputs,
89+
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
90+
) -> _ViewOutputs {
91+
preconditionFailure("TODO")
92+
}
93+
nonisolated public static func _makeViewList(
94+
modifier: _GraphValue<Self>,
95+
inputs: _ViewListInputs,
96+
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
97+
) -> _ViewListOutputs {
98+
preconditionFailure("TODO")
99+
}
100+
101+
nonisolated public static func _viewListCount(inputs: _ViewListCountInputs, body: (_ViewListCountInputs) -> Int?) -> Int? {
102+
preconditionFailure("TODO")
103+
}
104+
}
105+
106+
@available(*, unavailable)
107+
extension _ConditionalTraitWritingModifier: Sendable {}
108+
109+
extension View {
110+
@_alwaysEmitIntoClient
111+
public func _trait<K>(_ key: K.Type = K.self, _ value: K.Value, isEnabled: Bool) -> some View where K: _ViewTraitKey {
112+
modifier(_ConditionalTraitWritingModifier<K>(
113+
value: value,
114+
isEnabled: isEnabled
115+
))
116+
}
117+
}
118+
119+
// MARK: - TraitTransformerModifier [TODO]
120+
121+
struct TraitTransformerModifier {
122+
123+
}
124+
125+
extension View {
126+
package func transformTrait<K>(_ key: K.Type = K.self, transform: @escaping (inout K.Value) -> Void) -> some View where K: _ViewTraitKey {
127+
preconditionFailure("TODO")
128+
}
129+
}
130+
131+
// MARK: - ViewTraitCollection
132+
133+
private protocol AnyViewTrait {
134+
var id: ObjectIdentifier { get }
135+
subscript<V>() -> V { get set }
136+
}
137+
138+
package struct ViewTraitCollection {
139+
package init() {
140+
self.storage = []
141+
}
142+
143+
package func contains<Trait>(_ key: Trait.Type) -> Bool where Trait: _ViewTraitKey {
144+
storage.contains { $0.id == ObjectIdentifier(key) }
145+
}
146+
147+
package func value<Trait>(for key: Trait.Type, defaultValue: Trait.Value) -> Trait.Value where Trait: _ViewTraitKey {
148+
storage.first { $0.id == ObjectIdentifier(key) }?[] ?? defaultValue
149+
}
150+
151+
package func value<Trait>(for key: Trait.Type) -> Trait.Value where Trait: _ViewTraitKey {
152+
value(for: key, defaultValue: key.defaultValue)
153+
}
154+
155+
package mutating func setValueIfUnset<Trait>(_ value: Trait.Value, for key: Trait.Type) where Trait: _ViewTraitKey {
156+
guard !storage.contains(where: { $0.id == ObjectIdentifier(key) }) else {
157+
return
158+
}
159+
storage.append(AnyTrait<Trait>(value: value))
160+
}
161+
162+
package subscript<Trait>(key: Trait.Type) -> Trait.Value where Trait : _ViewTraitKey {
163+
get {
164+
value(for: key)
165+
}
166+
set {
167+
if let index = storage.firstIndex(where: { $0.id == ObjectIdentifier(key) }) {
168+
storage[index][] = newValue
169+
} else {
170+
storage.append(AnyTrait<Trait>(value: newValue))
171+
}
172+
}
173+
}
174+
175+
package mutating func mergeValues(_ traits: ViewTraitCollection) {
176+
for trait in traits.storage {
177+
setErasedValue(trait: trait)
178+
}
179+
}
180+
181+
private mutating func setErasedValue<ViewTrait>(trait: ViewTrait) where ViewTrait: AnyViewTrait {
182+
if let index = storage.firstIndex(where: { $0.id == trait.id }) {
183+
let value: Any = trait[]
184+
storage[index][] = value
185+
} else {
186+
storage.append(trait)
187+
}
188+
}
189+
190+
private var storage: [any AnyViewTrait]
191+
192+
private struct AnyTrait<Trait>: AnyViewTrait where Trait: _ViewTraitKey {
193+
typealias Value = Trait.Value
194+
195+
var value: Value
196+
197+
init(value: Trait.Value) {
198+
self.value = value
199+
}
200+
201+
var id: ObjectIdentifier { ObjectIdentifier(Trait.self) }
202+
203+
subscript<V>() -> V {
204+
get { value as! V }
205+
set { value = newValue as! Value }
206+
}
207+
}
208+
}
209+
210+
// MARK: - ViewTraitKeys
211+
212+
package struct ViewTraitKeys {
213+
package var types: Set<ObjectIdentifier>
214+
package var isDataDependent: Bool
215+
216+
package init() {
217+
types = []
218+
isDataDependent = false
219+
}
220+
221+
package func contains<T>(_ type: T.Type) -> Bool where T: _ViewTraitKey{
222+
types.contains(ObjectIdentifier(type))
223+
}
224+
225+
package mutating func insert<T>(_ type: T.Type) where T: _ViewTraitKey {
226+
types.insert(ObjectIdentifier(type))
227+
}
228+
229+
package mutating func formUnion(_ other: ViewTraitKeys) {
230+
types.formUnion(other.types)
231+
isDataDependent = isDataDependent || other.isDataDependent
232+
}
233+
234+
package func withDataDependent() -> ViewTraitKeys {
235+
var copy = self
236+
copy.isDataDependent = true
237+
return copy
238+
}
239+
}

Tests/OpenSwiftUICoreTests/View/AnyViewTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// AnyViewTests.swift
3-
// OpenSwiftUITests
3+
// OpenSwiftUICoreTests
44

55
@testable import OpenSwiftUICore
66
import Testing

0 commit comments

Comments
 (0)