Implement `DynamicProperty` (#213)
This commit is contained in:
parent
a24f49f298
commit
c68c70a7d7
|
@ -17,7 +17,7 @@
|
|||
|
||||
import OpenCombine
|
||||
|
||||
@propertyWrapper public struct AppStorage<Value>: ObservedProperty {
|
||||
@propertyWrapper public struct AppStorage<Value>: DynamicProperty {
|
||||
let provider: _StorageProvider?
|
||||
@Environment(\._defaultAppStorage) var defaultProvider: _StorageProvider?
|
||||
var unwrappedProvider: _StorageProvider {
|
||||
|
@ -51,7 +51,7 @@ import OpenCombine
|
|||
}
|
||||
}
|
||||
|
||||
extension AppStorage: DynamicProperty {}
|
||||
extension AppStorage: ObservedProperty {}
|
||||
|
||||
extension AppStorage {
|
||||
public init(wrappedValue: Value,
|
||||
|
|
|
@ -23,7 +23,7 @@ public enum _DefaultSceneStorageProvider {
|
|||
public static var `default`: _StorageProvider!
|
||||
}
|
||||
|
||||
@propertyWrapper public struct SceneStorage<Value>: ObservedProperty {
|
||||
@propertyWrapper public struct SceneStorage<Value>: DynamicProperty {
|
||||
let key: String
|
||||
let defaultValue: Value
|
||||
let store: (_StorageProvider, String, Value) -> ()
|
||||
|
@ -51,6 +51,8 @@ public enum _DefaultSceneStorageProvider {
|
|||
}
|
||||
}
|
||||
|
||||
extension SceneStorage: ObservedProperty {}
|
||||
|
||||
extension SceneStorage {
|
||||
public init(wrappedValue: Value,
|
||||
_ key: String) where Value == Bool {
|
||||
|
|
|
@ -15,5 +15,43 @@
|
|||
// Created by Carson Katri on 7/17/20.
|
||||
//
|
||||
|
||||
// FIXME: Match SwiftUI implementation
|
||||
protocol DynamicProperty {}
|
||||
import Runtime
|
||||
|
||||
public protocol DynamicProperty {
|
||||
mutating func update()
|
||||
}
|
||||
|
||||
extension DynamicProperty {
|
||||
public mutating func update() {}
|
||||
}
|
||||
|
||||
extension TypeInfo {
|
||||
/// Extract all `DynamicProperty` from a type, recursively.
|
||||
/// This is necessary as a `DynamicProperty` can be nested.
|
||||
/// `EnvironmentValues` can also be injected at this point.
|
||||
func dynamicProperties(_ environment: EnvironmentValues,
|
||||
source: inout Any,
|
||||
shouldUpdate: Bool) -> [PropertyInfo] {
|
||||
var dynamicProps = [PropertyInfo]()
|
||||
for prop in properties where prop.type is DynamicProperty.Type {
|
||||
dynamicProps.append(prop)
|
||||
// swiftlint:disable force_try
|
||||
let propInfo = try! typeInfo(of: prop.type)
|
||||
propInfo.injectEnvironment(from: environment, into: &source)
|
||||
var extracted = try! prop.get(from: source)
|
||||
dynamicProps.append(
|
||||
contentsOf: propInfo.dynamicProperties(environment,
|
||||
source: &extracted,
|
||||
shouldUpdate: shouldUpdate)
|
||||
)
|
||||
// swiftlint:disable:next force_cast
|
||||
var extractedDynamicProp = extracted as! DynamicProperty
|
||||
if shouldUpdate {
|
||||
extractedDynamicProp.update()
|
||||
}
|
||||
try! prop.set(value: extractedDynamicProp, on: &source)
|
||||
// swiftlint:enable force_try
|
||||
}
|
||||
return dynamicProps
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ protocol EnvironmentReader {
|
|||
mutating func setContent(from values: EnvironmentValues)
|
||||
}
|
||||
|
||||
@propertyWrapper public struct Environment<Value>: EnvironmentReader {
|
||||
@propertyWrapper public struct Environment<Value>: DynamicProperty {
|
||||
enum Content {
|
||||
case keyPath(KeyPath<EnvironmentValues, Value>)
|
||||
case value(Value)
|
||||
|
@ -50,3 +50,5 @@ protocol EnvironmentReader {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Environment: EnvironmentReader {}
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
import OpenCombine
|
||||
|
||||
@propertyWrapper public struct EnvironmentObject<ObjectType>: ObservedProperty,
|
||||
EnvironmentReader
|
||||
@propertyWrapper public struct EnvironmentObject<ObjectType>: DynamicProperty
|
||||
where ObjectType: ObservableObject {
|
||||
@dynamicMemberLookup public struct Wrapper {
|
||||
internal let root: ObjectType
|
||||
|
@ -63,6 +62,8 @@ import OpenCombine
|
|||
public init() {}
|
||||
}
|
||||
|
||||
extension EnvironmentObject: ObservedProperty, EnvironmentReader {}
|
||||
|
||||
extension ObservableObject {
|
||||
static var environmentStore: WritableKeyPath<EnvironmentValues, Self?> {
|
||||
\.[ObjectIdentifier(self)]
|
||||
|
|
|
@ -142,11 +142,17 @@ public final class StackReconciler<R: Renderer> {
|
|||
body bodyKeypath: ReferenceWritableKeyPath<MountedCompositeElement<R>, Any>,
|
||||
result: KeyPath<MountedCompositeElement<R>, (Any) -> T>) -> T {
|
||||
let info = try! typeInfo(of: compositeElement.elementType)
|
||||
info.injectEnvironment(from: compositeElement.environmentValues,
|
||||
into: &compositeElement[keyPath: bodyKeypath])
|
||||
|
||||
let needsSubscriptions = compositeElement.subscriptions.isEmpty
|
||||
|
||||
var stateIdx = 0
|
||||
for property in info.properties {
|
||||
let dynamicProps = info.dynamicProperties(compositeElement.environmentValues,
|
||||
source: &compositeElement[keyPath: bodyKeypath],
|
||||
shouldUpdate: true)
|
||||
for property in dynamicProps {
|
||||
// Setup state/subscriptions
|
||||
if property.type is ValueStorage.Type {
|
||||
setupState(id: stateIdx, for: property, of: compositeElement, body: bodyKeypath)
|
||||
stateIdx += 1
|
||||
|
|
|
@ -21,7 +21,7 @@ typealias Updater<T> = (inout T) -> ()
|
|||
view's state in-place synchronously, but only schedule an update with
|
||||
the renderer at a later time.
|
||||
*/
|
||||
@propertyWrapper public struct Binding<Value> {
|
||||
@propertyWrapper public struct Binding<Value>: DynamicProperty {
|
||||
public var wrappedValue: Value {
|
||||
get { get() }
|
||||
nonmutating set { set(newValue) }
|
||||
|
|
|
@ -22,7 +22,7 @@ protocol ObservedProperty: DynamicProperty {
|
|||
}
|
||||
|
||||
@propertyWrapper
|
||||
public struct ObservedObject<ObjectType>: ObservedProperty where ObjectType: ObservableObject {
|
||||
public struct ObservedObject<ObjectType>: DynamicProperty where ObjectType: ObservableObject {
|
||||
@dynamicMemberLookup
|
||||
public struct Wrapper {
|
||||
let root: ObjectType
|
||||
|
@ -52,4 +52,4 @@ public struct ObservedObject<ObjectType>: ObservedProperty where ObjectType: Obs
|
|||
}
|
||||
}
|
||||
|
||||
extension ObservedObject: DynamicProperty {}
|
||||
extension ObservedObject: ObservedProperty {}
|
||||
|
|
|
@ -14,14 +14,13 @@
|
|||
//
|
||||
// Created by Max Desiatov on 08/04/2020.
|
||||
//
|
||||
|
||||
protocol ValueStorage {
|
||||
var getter: (() -> Any)? { get set }
|
||||
var setter: ((Any) -> ())? { get set }
|
||||
var anyInitialValue: Any { get }
|
||||
}
|
||||
|
||||
@propertyWrapper public struct State<Value> {
|
||||
@propertyWrapper public struct State<Value>: DynamicProperty {
|
||||
private let initialValue: Value
|
||||
|
||||
var anyInitialValue: Any { initialValue }
|
||||
|
@ -48,7 +47,6 @@ protocol ValueStorage {
|
|||
}
|
||||
|
||||
extension State: ValueStorage {}
|
||||
extension State: DynamicProperty {}
|
||||
|
||||
extension State where Value: ExpressibleByNilLiteral {
|
||||
@inlinable public init() { self.init(wrappedValue: nil) }
|
||||
|
|
Loading…
Reference in New Issue