Skip to content

Commit ea17b0a

Browse files
authored
Add PreferenceKeys and update HostPreferencesKey (#35)
* Update gitignore file * Add PreferenceKeys and update Preference folder structure * Implement HostPreferencesKey reduce * Add HostPreferencesKeyTests test case
1 parent d8fb280 commit ea17b0a

File tree

12 files changed

+296
-11
lines changed

12 files changed

+296
-11
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ Example/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
1010
/.build-*
1111
/Benchmarks/.build
1212
/Example/.build
13-
Example/Package.resolved
13+
Example/Package.resolved
14+
TODO.md

Sources/OpenSwiftUI/DataAndStorage/Preferences/Internal/HostPreferencesKey.swift renamed to Sources/OpenSwiftUI/DataAndStorage/Preferences/HostPreferencesKey.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@
44
//
55
// Created by Kyle on 2023/1/6.
66
// Lastest Version: iOS 15.5
7-
// Status: WIP
7+
// Status: Complete
88
// ID: 7429200566949B8FB892A77E01A988C8
99

1010
struct HostPreferencesKey: PreferenceKey {
11-
static var defaultValue: PreferenceList {
12-
PreferenceList()
13-
}
11+
private static var nodeId: UInt32 = .zero
1412

15-
static func reduce(value: inout PreferenceList, nextValue: () -> PreferenceList) {
16-
// TODO:
13+
@inline(__always)
14+
static func makeNodeID() -> UInt32 {
15+
defer { nodeId &+= 1 }
16+
return nodeId
1717
}
18-
19-
private static var nodeId: UInt32 = .zero
2018
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// PreferenceKeys.swift
3+
// OpenSwiftUI
4+
//
5+
// Created by Kyle on 2024/2/2.
6+
// Lastest Version: iOS 15.5
7+
// Status: Complete
8+
9+
struct PreferenceKeys {
10+
private var keys: [AnyPreferenceKey.Type]
11+
12+
init() {
13+
self.keys = []
14+
}
15+
}
16+
17+
extension PreferenceKeys: RandomAccessCollection, MutableCollection {
18+
var startIndex: Int { keys.startIndex }
19+
var endIndex: Int { keys.endIndex }
20+
21+
mutating func add<Key: PreferenceKey>(_: Key.Type) {
22+
keys.append(_AnyPreferenceKey<Key>.self)
23+
}
24+
25+
mutating func add(_ key: AnyPreferenceKey.Type) {
26+
keys.append(key)
27+
}
28+
29+
func contains<Key: PreferenceKey>(_: Key.Type) -> Bool {
30+
contains(_AnyPreferenceKey<Key>.self)
31+
}
32+
33+
func contains(_ key: AnyPreferenceKey.Type) -> Bool {
34+
keys.contains { $0 == key }
35+
}
36+
37+
mutating func remove<Key: PreferenceKey>(_: Key.Type) {
38+
remove(_AnyPreferenceKey<Key>.self)
39+
}
40+
41+
mutating func remove(_ key: AnyPreferenceKey.Type) {
42+
for index in keys.indices {
43+
if keys[index] == key {
44+
keys.remove(at: index)
45+
return
46+
}
47+
}
48+
}
49+
50+
var isEmpty: Bool { keys.isEmpty }
51+
52+
subscript(position: Int) -> AnyPreferenceKey.Type {
53+
get { keys[position] }
54+
set { keys[position] = newValue }
55+
}
56+
}
57+
58+
extension PreferenceKeys: Equatable {
59+
static func == (lhs: PreferenceKeys, rhs: PreferenceKeys) -> Bool {
60+
guard lhs.keys.count == rhs.keys.count else {
61+
return false
62+
}
63+
guard !lhs.keys.isEmpty else {
64+
return true
65+
}
66+
for index in lhs.indices {
67+
guard lhs[index] == rhs[index] else {
68+
return false
69+
}
70+
}
71+
return true
72+
}
73+
}

Sources/OpenSwiftUI/DataAndStorage/Preferences/Internal/PreferenceList.swift renamed to Sources/OpenSwiftUI/DataAndStorage/Preferences/PreferenceList.swift

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// ID: C1C63C2F6F2B9F3EB30DD747F0605FBD
99

1010
struct PreferenceList: CustomStringConvertible {
11-
private var first: PreferenceNode?
11+
fileprivate var first: PreferenceNode?
1212

1313
subscript<Key: PreferenceKey>(_ keyType: Key.Type) -> Value<Key.Value> {
1414
get {
@@ -174,3 +174,34 @@ private class _PreferenceNode<Key: PreferenceKey>: PreferenceNode {
174174
"\(Key.self) = \(value)"
175175
}
176176
}
177+
178+
extension HostPreferencesKey {
179+
static var defaultValue: PreferenceList {
180+
PreferenceList()
181+
}
182+
183+
static func reduce(value: inout PreferenceList, nextValue: () -> PreferenceList) {
184+
let newValue = nextValue()
185+
guard let newFirst = newValue.first else {
186+
return
187+
}
188+
guard let first = value.first else {
189+
value.first = newFirst
190+
return
191+
}
192+
value.first = nil
193+
first.forEach { node in
194+
if let mergedNode = node.combine(from: newFirst, next: value.first) {
195+
value.first = mergedNode
196+
} else {
197+
value.first = node.copy(next: value.first)
198+
}
199+
}
200+
newFirst.forEach { node in
201+
guard node.find(from: first) == nil else {
202+
return
203+
}
204+
value.first = node.copy(next: value.first)
205+
}
206+
}
207+
}

Sources/OpenSwiftUI/DataAndStorage/Preferences/View_Preference.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Created by Kyle on 2023/9/24.
66
// Lastest Version: iOS 15.5
7-
// Status: WIP
7+
// Status: Complete
88

99
extension View {
1010
/// Sets a value for the given preference.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// HostPreferencesKeyTests.swift
3+
//
4+
//
5+
// Created by Kyle on 2024/2/4.
6+
//
7+
8+
@testable import OpenSwiftUI
9+
import Testing
10+
11+
struct HostPreferencesKeyTests {
12+
struct IntKey: PreferenceKey {
13+
static var defaultValue: Int { 0 }
14+
15+
static func reduce(value: inout Int, nextValue: () -> Int) {
16+
value += nextValue()
17+
}
18+
}
19+
20+
struct DoubleKey: PreferenceKey {
21+
static var defaultValue: Double { 0.0 }
22+
23+
static func reduce(value: inout Double, nextValue: () -> Double) {
24+
value += nextValue()
25+
}
26+
}
27+
28+
struct EnumKey: PreferenceKey {
29+
static func reduce(value: inout Value, nextValue: () -> Value) {
30+
let newValue = (value.rawValue + nextValue().rawValue) % Value.allCases.count
31+
value = .init(rawValue: newValue)!
32+
}
33+
34+
enum Value: Int, CaseIterable { case a, b, c, d }
35+
static var defaultValue: Value { .a }
36+
}
37+
38+
struct IntKey2: PreferenceKey {
39+
static var defaultValue: Int { 0 }
40+
41+
static func reduce(value: inout Int, nextValue: () -> Int) {
42+
value *= nextValue()
43+
}
44+
}
45+
46+
struct DoubleKey2: PreferenceKey {
47+
static var defaultValue: Double { 0.0 }
48+
49+
static func reduce(value: inout Double, nextValue: () -> Double) {
50+
value *= nextValue()
51+
}
52+
}
53+
54+
@Test
55+
func nodeID() {
56+
let id0 = HostPreferencesKey.makeNodeID()
57+
let id1 = HostPreferencesKey.makeNodeID()
58+
let id2 = HostPreferencesKey.makeNodeID()
59+
#expect(id1 == (id0 + 1))
60+
#expect(id2 == (id1 + 1))
61+
}
62+
63+
@Test(arguments: [
64+
([1, 2, 3, 4], [3, 12], [1.0, 2.0, 3.0, 4.0], [3.0, 12.0], [EnumKey.Value.a, .b, .c, .d], [EnumKey.Value.b, .a]),
65+
([0, 4, 5, 6], [4, 30], [0.0, 1.0, 2.0, 3.0], [1.0, 6.0], [EnumKey.Value.a, .c, .d, .b], [EnumKey.Value.c, .d]),
66+
([1, 7, 0, 5], [8, 0], [2.0, 1.5, 0.0, 5.0], [3.5, 0.0], [EnumKey.Value.c, .a, .b, .d], [EnumKey.Value.c, .b]),
67+
])
68+
func reduce(
69+
intValues: [Int], expectedIntValues: [Int],
70+
doubleValues: [Double], expectedDoubleValues: [Double],
71+
enumValues: [EnumKey.Value], expectedEnumValues: [EnumKey.Value]
72+
) {
73+
var value = HostPreferencesKey.defaultValue
74+
value[IntKey.self] = .init(value: intValues[0], seed: .init(value: 1))
75+
value[EnumKey.self] = .init(value: enumValues[0], seed: .init(value: 2))
76+
value[DoubleKey.self] = .init(value: doubleValues[0], seed: .init(value: 3))
77+
#expect(value.description == """
78+
372598479: [DoubleKey = \(doubleValues[0]), EnumKey = \(enumValues[0]), IntKey = \(intValues[0])]
79+
""")
80+
HostPreferencesKey.reduce(value: &value) {
81+
var nextValue = HostPreferencesKey.defaultValue
82+
nextValue[IntKey2.self] = .init(value: intValues[2], seed: .init(value: 4))
83+
nextValue[EnumKey.self] = .init(value: enumValues[1], seed: .init(value: 5))
84+
nextValue[DoubleKey2.self] = .init(value: doubleValues[2], seed: .init(value: 6))
85+
#expect(nextValue.description == """
86+
3126765658: [DoubleKey2 = \(doubleValues[2]), EnumKey = \(enumValues[1]), IntKey2 = \(intValues[2])]
87+
""")
88+
return nextValue
89+
}
90+
#expect(value.description == """
91+
1584719037: [IntKey2 = \(intValues[2]), DoubleKey2 = \(doubleValues[2]), IntKey = \(intValues[0]), EnumKey = \(expectedEnumValues[0]), DoubleKey = \(doubleValues[0])]
92+
""")
93+
HostPreferencesKey.reduce(value: &value) {
94+
var nextValue = HostPreferencesKey.defaultValue
95+
nextValue[DoubleKey.self] = .init(value: doubleValues[1], seed: .init(value: 7))
96+
nextValue[EnumKey.self] = .init(value: enumValues[2], seed: .init(value: 8))
97+
nextValue[IntKey.self] = .init(value: intValues[1], seed: .init(value: 9))
98+
#expect(nextValue.description == """
99+
1190677337: [IntKey = \(intValues[1]), EnumKey = \(enumValues[2]), DoubleKey = \(doubleValues[1])]
100+
""")
101+
nextValue[DoubleKey2.self] = .init(value: doubleValues[3], seed: .init(value: 7))
102+
nextValue[EnumKey.self] = .init(value: enumValues[3], seed: .init(value: 8))
103+
nextValue[IntKey2.self] = .init(value: intValues[3], seed: .init(value: 9))
104+
#expect(nextValue.description == """
105+
1148928659: [IntKey2 = \(intValues[3]), EnumKey = \(enumValues[3]), DoubleKey = \(doubleValues[1]), IntKey = \(intValues[1]), DoubleKey2 = \(doubleValues[3])]
106+
""")
107+
return nextValue
108+
}
109+
#expect(value.description == """
110+
2384310348: [DoubleKey = \(expectedDoubleValues[0]), EnumKey = \(expectedEnumValues[1]), IntKey = \(expectedIntValues[0]), DoubleKey2 = \(expectedDoubleValues[1]), IntKey2 = \(expectedIntValues[1])]
111+
""")
112+
}
113+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// PreferenceListTests.swift
3+
//
4+
//
5+
// Created by Kyle on 2024/2/4.
6+
//
7+
8+
@testable import OpenSwiftUI
9+
import Testing
10+
11+
struct PreferenceListTests {
12+
struct IntKey: PreferenceKey {
13+
static var defaultValue: Int { 0 }
14+
15+
static func reduce(value: inout Int, nextValue: () -> Int) {
16+
value += nextValue()
17+
}
18+
}
19+
20+
struct DoubleKey: PreferenceKey {
21+
static var defaultValue: Double { 0.0 }
22+
23+
static func reduce(value: inout Double, nextValue: () -> Double) {
24+
value += nextValue()
25+
}
26+
}
27+
28+
struct EnumKey: PreferenceKey {
29+
static func reduce(value: inout Value, nextValue: () -> Value) {
30+
value = nextValue()
31+
}
32+
33+
enum Value { case a, b }
34+
static var defaultValue: Value { .a }
35+
}
36+
37+
38+
@Test("Test description and subscript with zero seed")
39+
func subscriptAndDescriptionWithZeroSeed() {
40+
var list = PreferenceList()
41+
#expect(list.description == "empty: []")
42+
list[IntKey.self] = PreferenceList.Value(value: 1, seed: .zero)
43+
#expect(list.description == "empty: [IntKey = 1]")
44+
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .zero)
45+
#expect(list.description == "empty: [DoubleKey = 1.0, IntKey = 1]")
46+
list[IntKey.self] = PreferenceList.Value(value: 2, seed: .zero)
47+
#expect(list.description == "empty: [IntKey = 2, DoubleKey = 1.0]")
48+
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .zero)
49+
#expect(list.description == "empty: [DoubleKey = 1.0, IntKey = 2]")
50+
list[EnumKey.self] = PreferenceList.Value(value: .a, seed: .zero)
51+
#expect(list.description == "empty: [EnumKey = a, DoubleKey = 1.0, IntKey = 2]")
52+
}
53+
54+
@Test("Test description and subscript with seed")
55+
func subscriptAndDescriptionWithSeed() {
56+
var list = PreferenceList()
57+
#expect(list.description == "empty: []")
58+
list[IntKey.self] = PreferenceList.Value(value: 1, seed: .init(value: 1))
59+
#expect(list.description == "1: [IntKey = 1]")
60+
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .init(value: 2))
61+
#expect(list.description == "547159728: [DoubleKey = 1.0, IntKey = 1]")
62+
list[IntKey.self] = PreferenceList.Value(value: 2, seed: .init(value: 3))
63+
#expect(list.description == "3634229150: [IntKey = 2, DoubleKey = 1.0]")
64+
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .init(value: 4))
65+
#expect(list.description == "1218402493: [DoubleKey = 1.0, IntKey = 2]")
66+
list[EnumKey.self] = PreferenceList.Value(value: .a, seed: .init(value: 5))
67+
#expect(list.description == "1817264013: [EnumKey = a, DoubleKey = 1.0, IntKey = 2]")
68+
}
69+
}

0 commit comments

Comments
 (0)