Skip to content

Commit 39941be

Browse files
committed
Complete State implementation
1 parent cfb94e2 commit 39941be

File tree

6 files changed

+122
-26
lines changed

6 files changed

+122
-26
lines changed

Package.resolved

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"location" : "https://github.com/OpenSwiftUIProject/OpenGraph",
88
"state" : {
99
"branch" : "main",
10-
"revision" : "4eebdd7353a8fdd1c7f5756e13dbdf73ed3055a1"
10+
"revision" : "036b8d28a8a1220a768a81bbc20624759094137c"
1111
}
1212
},
1313
{

Sources/OpenSwiftUI/Core/Data/Property/PropertyList.swift

+17-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Status: Blocked by merge
77
// ID: 2B32D570B0B3D2A55DA9D4BFC1584D20
88

9+
internal import COpenSwiftUI
910
internal import OpenGraphShims
1011

1112
// MARK: - PropertyList
@@ -18,6 +19,11 @@ struct PropertyList: CustomStringConvertible {
1819

1920
@inlinable
2021
init() { elements = nil }
22+
23+
@inline(__always)
24+
init(elements: Element?) {
25+
self.elements = elements
26+
}
2127

2228
@usableFromInline
2329
var description: String {
@@ -86,13 +92,23 @@ struct PropertyList: CustomStringConvertible {
8692
fatalError("TODO")
8793
}
8894

89-
mutating private func override(with plist: PropertyList) {
95+
mutating func override(with plist: PropertyList) {
9096
if let element = elements {
9197
elements = element.byPrepending(plist.elements)
9298
} else {
9399
elements = plist.elements
94100
}
95101
}
102+
103+
@inline(__always)
104+
static var current: PropertyList {
105+
if let data = _threadTransactionData() {
106+
// FIXME: swift_dynamicCastClassUnconditional
107+
PropertyList(elements: data.assumingMemoryBound(to: Element.self).pointee)
108+
} else {
109+
PropertyList()
110+
}
111+
}
96112
}
97113

98114
// MARK: - PropertyList Help functions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// ThreadUtils.swift
3+
//
4+
//
5+
// Created by Kyle on 2024/4/21.
6+
//
7+
8+
import Foundation
9+
10+
@inline(__always)
11+
func performOnMainThread(_ block: @escaping () -> Void) {
12+
if Thread.isMainThread {
13+
block()
14+
} else {
15+
RunLoop.main.perform(inModes: [.common], block: block)
16+
}
17+
}

Sources/OpenSwiftUI/Data/Model/State/State.swift

+21-10
Original file line numberDiff line numberDiff line change
@@ -253,19 +253,30 @@ private struct StatePropertyBox<Value>: DynamicPropertyBox {
253253
var location: StoredLocation<Value>?
254254

255255
typealias Property = State<Value>
256-
257256
func destroy() {}
258-
259257
mutating func reset() { location = nil }
260-
261258
mutating func update(property: inout State<Value>, phase: _GraphInputs.Phase) -> Bool {
262-
// TODO
263-
false
259+
let locationChanged = location == nil
260+
if location == nil {
261+
location = property._location as? StoredLocation ?? StoredLocation(
262+
initialValue: property._value,
263+
host: .currentHost,
264+
signal: signal
265+
)
266+
}
267+
let signalChanged = signal.changedValue()?.changed ?? false
268+
property._value = location!.updateValue
269+
property._location = location!
270+
return (signalChanged ? location!.wasRead : false) || locationChanged
264271
}
265-
266-
func getState<V>(type: V.Type) -> Binding<V>? {
267-
// TODO
268-
nil
272+
func getState<V>(type _: V.Type) -> Binding<V>? {
273+
guard Value.self == V.self,
274+
let location
275+
else {
276+
return nil
277+
}
278+
let value = location.get()
279+
let binding = Binding(value: value, location: location)
280+
return binding as? Binding<V>
269281
}
270282
}
271-

Sources/OpenSwiftUI/Data/Model/State/StoredLocation.swift

+47-10
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class StoredLocationBase<Value>: AnyLocation<Value>, Location {
2222
}
2323
}
2424

25-
private struct BeginUpdate: GraphMutation {
25+
fileprivate struct BeginUpdate: GraphMutation {
2626
weak var box: StoredLocationBase<Value>?
2727

2828
func apply() {
@@ -56,21 +56,19 @@ class StoredLocationBase<Value>: AnyLocation<Value>, Location {
5656
super.init()
5757
}
5858

59-
private var isValid: Bool {
60-
true
61-
}
59+
fileprivate var isValid: Bool { true }
6260

6361
// MARK: - abstract method
6462

65-
private var isUpdating: Bool {
63+
fileprivate var isUpdating: Bool {
6664
fatalError("abstract")
6765
}
6866

69-
private func commit(transaction: Transaction, mutation: BeginUpdate) {
67+
fileprivate func commit(transaction: Transaction, mutation: BeginUpdate) {
7068
fatalError("abstract")
7169
}
7270

73-
private func notifyObservers() {
71+
fileprivate func notifyObservers() {
7472
fatalError("abstract")
7573
}
7674

@@ -96,15 +94,26 @@ class StoredLocationBase<Value>: AnyLocation<Value>, Location {
9694
}
9795
return
9896
}
99-
let _ = $data.withMutableData { data in
97+
let shouldCommit = $data.withMutableData { data in
10098
guard !compareValues(data.currentValue, value) else {
10199
return false
102100
}
103101
data.savedValue.append(data.currentValue)
104102
data.currentValue = value
105103
return true
106104
}
107-
// TODO
105+
guard shouldCommit else {
106+
return
107+
}
108+
var newTransaction = transaction
109+
newTransaction.override(.current)
110+
performOnMainThread { [weak self] in
111+
guard let self else {
112+
return
113+
}
114+
let update = BeginUpdate(box: self)
115+
commit(transaction: newTransaction, mutation: update)
116+
}
108117
}
109118

110119
override func projecting<P: Projection>(_ projection: P) -> AnyLocation<P.Projected> where Value == P.Base {
@@ -130,10 +139,38 @@ class StoredLocationBase<Value>: AnyLocation<Value>, Location {
130139

131140
private final func beginUpdate() {
132141
data.savedValue.removeFirst()
142+
notifyObservers()
133143
}
134144
}
135145

136-
// TODO
137146
final class StoredLocation<Value>: StoredLocationBase<Value> {
147+
weak var host: GraphHost?
148+
@WeakAttribute var signal: Void?
149+
150+
init(initialValue value: Value, host: GraphHost?, signal: WeakAttribute<Void>) {
151+
self.host = host
152+
_signal = signal
153+
super.init(initialValue: value)
154+
}
138155

156+
override fileprivate var isValid: Bool {
157+
host?.isValid ?? false
158+
}
159+
160+
override fileprivate var isUpdating: Bool {
161+
host?.isUpdating ?? false
162+
}
163+
164+
override fileprivate func commit(transaction: Transaction, mutation: StoredLocationBase<Value>.BeginUpdate) {
165+
host?.asyncTransaction(
166+
transaction,
167+
mutation: mutation,
168+
style: ._1,
169+
mayDeferUpdate: true
170+
)
171+
}
172+
173+
override fileprivate func notifyObservers() {
174+
$signal?.invalidateValue()
175+
}
139176
}

Sources/OpenSwiftUI/View/Transaction/Transaction.swift

+19-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,26 @@ public struct Transaction {
2222
plist = PropertyList()
2323
}
2424

25+
@inline(__always)
26+
init(plist: PropertyList) {
27+
self.plist = plist
28+
}
29+
2530
@usableFromInline
2631
var plist: PropertyList
2732

2833
@inline(__always)
29-
@inlinable
3034
var isEmpty: Bool { plist.elements == nil }
35+
36+
@inline(__always)
37+
mutating func override(_ transaction: Transaction) {
38+
plist.override(with: transaction.plist)
39+
}
40+
41+
@inline(__always)
42+
static var current: Transaction {
43+
Transaction(plist: .current)
44+
}
3145
}
3246

3347
extension Transaction {
@@ -55,9 +69,10 @@ public func withTransaction<Result>(
5569
_ body: () throws -> Result
5670
) rethrows -> Result {
5771
try withExtendedLifetime(transaction) {
58-
let data = _threadTransactionData()
59-
defer { _setThreadTransactionData(data) }
60-
_setThreadTransactionData(transaction.plist.elements.map { Unmanaged.passUnretained($0).toOpaque() })
72+
let oldData = _threadTransactionData()
73+
defer { _setThreadTransactionData(oldData) }
74+
let data = transaction.plist.elements.map { Unmanaged.passUnretained($0).toOpaque() }
75+
_setThreadTransactionData(data)
6176
return try body()
6277
}
6378
}

0 commit comments

Comments
 (0)