Skip to content

Commit

Permalink
Add PreferenceKeys and update HostPreferencesKey (#35)
Browse files Browse the repository at this point in the history
* Update gitignore file

* Add PreferenceKeys and update Preference folder structure

* Implement HostPreferencesKey reduce

* Add HostPreferencesKeyTests test case
  • Loading branch information
Kyle-Ye authored Feb 4, 2024
1 parent d8fb280 commit ea17b0a
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 11 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ Example/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
/.build-*
/Benchmarks/.build
/Example/.build
Example/Package.resolved
Example/Package.resolved
TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
//
// Created by Kyle on 2023/1/6.
// Lastest Version: iOS 15.5
// Status: WIP
// Status: Complete
// ID: 7429200566949B8FB892A77E01A988C8

struct HostPreferencesKey: PreferenceKey {
static var defaultValue: PreferenceList {
PreferenceList()
}
private static var nodeId: UInt32 = .zero

static func reduce(value: inout PreferenceList, nextValue: () -> PreferenceList) {
// TODO:
@inline(__always)
static func makeNodeID() -> UInt32 {
defer { nodeId &+= 1 }
return nodeId
}

private static var nodeId: UInt32 = .zero
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// PreferenceKeys.swift
// OpenSwiftUI
//
// Created by Kyle on 2024/2/2.
// Lastest Version: iOS 15.5
// Status: Complete

struct PreferenceKeys {
private var keys: [AnyPreferenceKey.Type]

init() {
self.keys = []
}
}

extension PreferenceKeys: RandomAccessCollection, MutableCollection {
var startIndex: Int { keys.startIndex }
var endIndex: Int { keys.endIndex }

mutating func add<Key: PreferenceKey>(_: Key.Type) {
keys.append(_AnyPreferenceKey<Key>.self)
}

mutating func add(_ key: AnyPreferenceKey.Type) {
keys.append(key)
}

func contains<Key: PreferenceKey>(_: Key.Type) -> Bool {
contains(_AnyPreferenceKey<Key>.self)
}

func contains(_ key: AnyPreferenceKey.Type) -> Bool {
keys.contains { $0 == key }
}

mutating func remove<Key: PreferenceKey>(_: Key.Type) {
remove(_AnyPreferenceKey<Key>.self)
}

mutating func remove(_ key: AnyPreferenceKey.Type) {
for index in keys.indices {
if keys[index] == key {
keys.remove(at: index)
return
}
}
}

var isEmpty: Bool { keys.isEmpty }

subscript(position: Int) -> AnyPreferenceKey.Type {
get { keys[position] }
set { keys[position] = newValue }
}
}

extension PreferenceKeys: Equatable {
static func == (lhs: PreferenceKeys, rhs: PreferenceKeys) -> Bool {
guard lhs.keys.count == rhs.keys.count else {
return false
}
guard !lhs.keys.isEmpty else {
return true
}
for index in lhs.indices {
guard lhs[index] == rhs[index] else {
return false
}
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// ID: C1C63C2F6F2B9F3EB30DD747F0605FBD

struct PreferenceList: CustomStringConvertible {
private var first: PreferenceNode?
fileprivate var first: PreferenceNode?

subscript<Key: PreferenceKey>(_ keyType: Key.Type) -> Value<Key.Value> {
get {
Expand Down Expand Up @@ -174,3 +174,34 @@ private class _PreferenceNode<Key: PreferenceKey>: PreferenceNode {
"\(Key.self) = \(value)"
}
}

extension HostPreferencesKey {
static var defaultValue: PreferenceList {
PreferenceList()
}

static func reduce(value: inout PreferenceList, nextValue: () -> PreferenceList) {
let newValue = nextValue()
guard let newFirst = newValue.first else {
return
}
guard let first = value.first else {
value.first = newFirst
return
}
value.first = nil
first.forEach { node in
if let mergedNode = node.combine(from: newFirst, next: value.first) {
value.first = mergedNode
} else {
value.first = node.copy(next: value.first)
}
}
newFirst.forEach { node in
guard node.find(from: first) == nil else {
return
}
value.first = node.copy(next: value.first)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// Created by Kyle on 2023/9/24.
// Lastest Version: iOS 15.5
// Status: WIP
// Status: Complete

extension View {
/// Sets a value for the given preference.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// HostPreferencesKeyTests.swift
//
//
// Created by Kyle on 2024/2/4.
//

@testable import OpenSwiftUI
import Testing

struct HostPreferencesKeyTests {
struct IntKey: PreferenceKey {
static var defaultValue: Int { 0 }

static func reduce(value: inout Int, nextValue: () -> Int) {
value += nextValue()
}
}

struct DoubleKey: PreferenceKey {
static var defaultValue: Double { 0.0 }

static func reduce(value: inout Double, nextValue: () -> Double) {
value += nextValue()
}
}

struct EnumKey: PreferenceKey {
static func reduce(value: inout Value, nextValue: () -> Value) {
let newValue = (value.rawValue + nextValue().rawValue) % Value.allCases.count
value = .init(rawValue: newValue)!
}

enum Value: Int, CaseIterable { case a, b, c, d }
static var defaultValue: Value { .a }
}

struct IntKey2: PreferenceKey {
static var defaultValue: Int { 0 }

static func reduce(value: inout Int, nextValue: () -> Int) {
value *= nextValue()
}
}

struct DoubleKey2: PreferenceKey {
static var defaultValue: Double { 0.0 }

static func reduce(value: inout Double, nextValue: () -> Double) {
value *= nextValue()
}
}

@Test
func nodeID() {
let id0 = HostPreferencesKey.makeNodeID()
let id1 = HostPreferencesKey.makeNodeID()
let id2 = HostPreferencesKey.makeNodeID()
#expect(id1 == (id0 + 1))
#expect(id2 == (id1 + 1))
}

@Test(arguments: [
([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]),
([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]),
([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]),
])
func reduce(
intValues: [Int], expectedIntValues: [Int],
doubleValues: [Double], expectedDoubleValues: [Double],
enumValues: [EnumKey.Value], expectedEnumValues: [EnumKey.Value]
) {
var value = HostPreferencesKey.defaultValue
value[IntKey.self] = .init(value: intValues[0], seed: .init(value: 1))
value[EnumKey.self] = .init(value: enumValues[0], seed: .init(value: 2))
value[DoubleKey.self] = .init(value: doubleValues[0], seed: .init(value: 3))
#expect(value.description == """
372598479: [DoubleKey = \(doubleValues[0]), EnumKey = \(enumValues[0]), IntKey = \(intValues[0])]
""")
HostPreferencesKey.reduce(value: &value) {
var nextValue = HostPreferencesKey.defaultValue
nextValue[IntKey2.self] = .init(value: intValues[2], seed: .init(value: 4))
nextValue[EnumKey.self] = .init(value: enumValues[1], seed: .init(value: 5))
nextValue[DoubleKey2.self] = .init(value: doubleValues[2], seed: .init(value: 6))
#expect(nextValue.description == """
3126765658: [DoubleKey2 = \(doubleValues[2]), EnumKey = \(enumValues[1]), IntKey2 = \(intValues[2])]
""")
return nextValue
}
#expect(value.description == """
1584719037: [IntKey2 = \(intValues[2]), DoubleKey2 = \(doubleValues[2]), IntKey = \(intValues[0]), EnumKey = \(expectedEnumValues[0]), DoubleKey = \(doubleValues[0])]
""")
HostPreferencesKey.reduce(value: &value) {
var nextValue = HostPreferencesKey.defaultValue
nextValue[DoubleKey.self] = .init(value: doubleValues[1], seed: .init(value: 7))
nextValue[EnumKey.self] = .init(value: enumValues[2], seed: .init(value: 8))
nextValue[IntKey.self] = .init(value: intValues[1], seed: .init(value: 9))
#expect(nextValue.description == """
1190677337: [IntKey = \(intValues[1]), EnumKey = \(enumValues[2]), DoubleKey = \(doubleValues[1])]
""")
nextValue[DoubleKey2.self] = .init(value: doubleValues[3], seed: .init(value: 7))
nextValue[EnumKey.self] = .init(value: enumValues[3], seed: .init(value: 8))
nextValue[IntKey2.self] = .init(value: intValues[3], seed: .init(value: 9))
#expect(nextValue.description == """
1148928659: [IntKey2 = \(intValues[3]), EnumKey = \(enumValues[3]), DoubleKey = \(doubleValues[1]), IntKey = \(intValues[1]), DoubleKey2 = \(doubleValues[3])]
""")
return nextValue
}
#expect(value.description == """
2384310348: [DoubleKey = \(expectedDoubleValues[0]), EnumKey = \(expectedEnumValues[1]), IntKey = \(expectedIntValues[0]), DoubleKey2 = \(expectedDoubleValues[1]), IntKey2 = \(expectedIntValues[1])]
""")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// PreferenceListTests.swift
//
//
// Created by Kyle on 2024/2/4.
//

@testable import OpenSwiftUI
import Testing

struct PreferenceListTests {
struct IntKey: PreferenceKey {
static var defaultValue: Int { 0 }

static func reduce(value: inout Int, nextValue: () -> Int) {
value += nextValue()
}
}

struct DoubleKey: PreferenceKey {
static var defaultValue: Double { 0.0 }

static func reduce(value: inout Double, nextValue: () -> Double) {
value += nextValue()
}
}

struct EnumKey: PreferenceKey {
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}

enum Value { case a, b }
static var defaultValue: Value { .a }
}


@Test("Test description and subscript with zero seed")
func subscriptAndDescriptionWithZeroSeed() {
var list = PreferenceList()
#expect(list.description == "empty: []")
list[IntKey.self] = PreferenceList.Value(value: 1, seed: .zero)
#expect(list.description == "empty: [IntKey = 1]")
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .zero)
#expect(list.description == "empty: [DoubleKey = 1.0, IntKey = 1]")
list[IntKey.self] = PreferenceList.Value(value: 2, seed: .zero)
#expect(list.description == "empty: [IntKey = 2, DoubleKey = 1.0]")
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .zero)
#expect(list.description == "empty: [DoubleKey = 1.0, IntKey = 2]")
list[EnumKey.self] = PreferenceList.Value(value: .a, seed: .zero)
#expect(list.description == "empty: [EnumKey = a, DoubleKey = 1.0, IntKey = 2]")
}

@Test("Test description and subscript with seed")
func subscriptAndDescriptionWithSeed() {
var list = PreferenceList()
#expect(list.description == "empty: []")
list[IntKey.self] = PreferenceList.Value(value: 1, seed: .init(value: 1))
#expect(list.description == "1: [IntKey = 1]")
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .init(value: 2))
#expect(list.description == "547159728: [DoubleKey = 1.0, IntKey = 1]")
list[IntKey.self] = PreferenceList.Value(value: 2, seed: .init(value: 3))
#expect(list.description == "3634229150: [IntKey = 2, DoubleKey = 1.0]")
list[DoubleKey.self] = PreferenceList.Value(value: 1.0, seed: .init(value: 4))
#expect(list.description == "1218402493: [DoubleKey = 1.0, IntKey = 2]")
list[EnumKey.self] = PreferenceList.Value(value: .a, seed: .init(value: 5))
#expect(list.description == "1817264013: [EnumKey = a, DoubleKey = 1.0, IntKey = 2]")
}
}

0 comments on commit ea17b0a

Please sign in to comment.