From 3e058c12dbb5461cafe25ab87f5d52fb1785a182 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 5 Nov 2023 17:11:12 +0800 Subject: [PATCH] Add ObservedObject implementation --- .../Location/ObservableObjectLocation.swift | 40 ++++++++++++ .../Internal/Location/TODO/LocationBox.swift | 8 +++ .../Internal/TODO/PropertyList.swift | 4 ++ .../ModelData/State/ObservedObject.swift | 61 +++++++++++++++++++ .../Internal/Other/ProtocolDescriptor.swift | 1 + .../Views/Animations/TODO/Transaction.swift | 22 +++++-- Sources/OpenSwiftUIShims/TLS.c | 20 ++++++ Sources/OpenSwiftUIShims/include/TLS.h | 20 ++++++ 8 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 Sources/OpenSwiftUI/DataAndStorage/Internal/Location/ObservableObjectLocation.swift create mode 100644 Sources/OpenSwiftUI/DataAndStorage/ModelData/State/ObservedObject.swift create mode 100644 Sources/OpenSwiftUIShims/TLS.c create mode 100644 Sources/OpenSwiftUIShims/include/TLS.h diff --git a/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/ObservableObjectLocation.swift b/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/ObservableObjectLocation.swift new file mode 100644 index 0000000..ce5cf1d --- /dev/null +++ b/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/ObservableObjectLocation.swift @@ -0,0 +1,40 @@ +// +// ObservableObjectLocation.swift +// OpenSwiftUI +// +// Created by Kyle on 2023/11/2. +// Lastest Version: iOS 15.5 +// Status: Blocked by PropertyList.Element + +#if OPENSWIFTUI_USE_COMBINE +import Combine +#else +import OpenCombine +#endif +internal import OpenSwiftUIShims + +struct ObservableObjectLocation: Location where Root: ObservableObject { + let base: Root + let keyPath: ReferenceWritableKeyPath + + var wasRead: Bool { + get { true } + set {} + } + func get() -> Value { base[keyPath: keyPath] } + // FIXME + func set(_ value: Value, transaction: Transaction) { + let element = _threadTransactionData().map { Unmanaged.fromOpaque($0).takeRetainedValue() } + let newElement: PropertyList.Element? + if let element = transaction.plist.elements { + newElement = element.byPrepending(element) + } else { + newElement = element + } + + let data = _threadTransactionData() + defer { _setThreadTransactionData(data) } + _setThreadTransactionData(newElement.map { Unmanaged.passUnretained($0).toOpaque() }) + base[keyPath: keyPath] = value + } +} diff --git a/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/TODO/LocationBox.swift b/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/TODO/LocationBox.swift index aad8ea7..cd99f0c 100644 --- a/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/TODO/LocationBox.swift +++ b/Sources/OpenSwiftUI/DataAndStorage/Internal/Location/TODO/LocationBox.swift @@ -14,4 +14,12 @@ class LocationBox: AnyLocation { init(location: L) { self.location = location } + + override func get() -> L.Value { + location.get() + } + + override func set(_ value: L.Value, transaction: Transaction) { + location.set(value, transaction: transaction) + } } diff --git a/Sources/OpenSwiftUI/DataAndStorage/Internal/TODO/PropertyList.swift b/Sources/OpenSwiftUI/DataAndStorage/Internal/TODO/PropertyList.swift index e579ae0..57a4149 100644 --- a/Sources/OpenSwiftUI/DataAndStorage/Internal/TODO/PropertyList.swift +++ b/Sources/OpenSwiftUI/DataAndStorage/Internal/TODO/PropertyList.swift @@ -49,6 +49,10 @@ extension PropertyList { /*@objc*/ @usableFromInline deinit {} + + func byPrepending(_ element: Element?) -> Element { + self + } } } diff --git a/Sources/OpenSwiftUI/DataAndStorage/ModelData/State/ObservedObject.swift b/Sources/OpenSwiftUI/DataAndStorage/ModelData/State/ObservedObject.swift new file mode 100644 index 0000000..4132929 --- /dev/null +++ b/Sources/OpenSwiftUI/DataAndStorage/ModelData/State/ObservedObject.swift @@ -0,0 +1,61 @@ +// +// ObservedObject.swift +// OpenSwiftUI +// +// Created by Kyle on 2023/11/5. +// Lastest Version: iOS 15.5 +// Status: Blocked by DynamicProperty + +#if OPENSWIFTUI_USE_COMBINE +import Combine +#else +import OpenCombine +#endif +internal import OpenSwiftUIShims + +@propertyWrapper +@frozen +public struct ObservedObject where ObjectType: ObservableObject { + @dynamicMemberLookup + @frozen + public struct Wrapper { + let root: ObjectType + public subscript(dynamicMember keyPath: ReferenceWritableKeyPath) -> Binding { + Binding(root, keyPath: keyPath) + } + } + + @usableFromInline + var _seed = 0 + + public var wrappedValue: ObjectType + + @_alwaysEmitIntoClient + public init(initialValue: ObjectType) { + self.init(wrappedValue: initialValue) + } + + public init(wrappedValue: ObjectType) { + self.wrappedValue = wrappedValue + } + + public var projectedValue: ObservedObject.Wrapper { + .init(root: wrappedValue) + } +} + +extension ObservedObject: DynamicProperty { + public static func _makeProperty(in _: inout _DynamicPropertyBuffer, container _: _GraphValue, fieldOffset _: Int, inputs _: inout _GraphInputs) { + // TODO + } + + public static var _propertyBehaviors: UInt32 { 2 } +} + +extension Binding { + init(_ root: ObjectType, keyPath: ReferenceWritableKeyPath) { + let location = ObservableObjectLocation(base: root, keyPath: keyPath) + let box = LocationBox(location: location) + self.init(value: location.get(), location: box) + } +} diff --git a/Sources/OpenSwiftUI/Internal/Other/ProtocolDescriptor.swift b/Sources/OpenSwiftUI/Internal/Other/ProtocolDescriptor.swift index cba6913..2e51148 100644 --- a/Sources/OpenSwiftUI/Internal/Other/ProtocolDescriptor.swift +++ b/Sources/OpenSwiftUI/Internal/Other/ProtocolDescriptor.swift @@ -2,6 +2,7 @@ func conformsToProtocol(_ type: Any.Type, _ protocolDescriptor: UnsafeRawPointer swiftConformsToProtocol(type, protocolDescriptor) != nil } +// FIXME: This kind usage of @_silgen_name is discouraged. But I'm not finding a way to declare or pass Any.Type to C @_silgen_name("swift_conformsToProtocol") @inline(__always) private func swiftConformsToProtocol(_ type: Any.Type, diff --git a/Sources/OpenSwiftUI/Views/Animations/TODO/Transaction.swift b/Sources/OpenSwiftUI/Views/Animations/TODO/Transaction.swift index 2833592..76b2e6f 100644 --- a/Sources/OpenSwiftUI/Views/Animations/TODO/Transaction.swift +++ b/Sources/OpenSwiftUI/Views/Animations/TODO/Transaction.swift @@ -7,6 +7,8 @@ // Status: Empty // ID: 39EC6D46662E6D7A6963F5C611934B0A +internal import OpenSwiftUIShims + @frozen public struct Transaction { @usableFromInline @@ -34,19 +36,19 @@ extension Transaction { // } } -//extension Transaction { +// extension Transaction { // public var isContinuous: Bool { // get // set // } -//} +// } // -//extension Transaction { +// extension Transaction { // public var _scrollViewAnimates: _ScrollViewAnimationMode { // get // set // } -//} +// } protocol TransactionKey { associatedtype Value @@ -56,3 +58,15 @@ protocol TransactionKey { private struct AnimationKey: TransactionKey { static let defaultValue: Animation? = nil } + +public func withTransaction( + _ transaction: Transaction, + _ body: () throws -> Result +) rethrows -> Result { + try withExtendedLifetime(transaction) { + let data = _threadTransactionData() + defer { _setThreadTransactionData(data) } + _setThreadTransactionData(transaction.plist.elements.map { Unmanaged.passUnretained($0).toOpaque() }) + return try body() + } +} diff --git a/Sources/OpenSwiftUIShims/TLS.c b/Sources/OpenSwiftUIShims/TLS.c new file mode 100644 index 0000000..8583b11 --- /dev/null +++ b/Sources/OpenSwiftUIShims/TLS.c @@ -0,0 +1,20 @@ +// +// TLS.c +// +// +// Created by Kyle on 2023/11/5. +// + +#include "TLS.h" + +static _Thread_local int64_t _perThreadUpdateCount = 0; +static _Thread_local void * _perThreadTransactionData = NULL; +static _Thread_local void * _perThreadGeometryProxyData = NULL; + +void _setThreadTransactionData(void * data) { + _perThreadTransactionData = data; +} + +void * _threadTransactionData(void) { + return _perThreadTransactionData; +} diff --git a/Sources/OpenSwiftUIShims/include/TLS.h b/Sources/OpenSwiftUIShims/include/TLS.h new file mode 100644 index 0000000..682092f --- /dev/null +++ b/Sources/OpenSwiftUIShims/include/TLS.h @@ -0,0 +1,20 @@ +// +// TLS.h +// +// +// Created by Kyle on 2023/11/5. +// + +#ifndef TLS_h +#define TLS_h + +#include +#include + +OF_EXPORT +void _setThreadTransactionData(void * _Nullable data); + +OF_EXPORT +void * _Nullable _threadTransactionData(void); + +#endif /* TLS_h */