Last active
June 29, 2020 15:31
-
-
Save maximkrouk/7b7f7a8882e5f58518db84b0a3f29ea5 to your computer and use it in GitHub Desktop.
Wrappers for value & reference types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#ref--box | |
#if canImport(Combine) | |
import Combine | |
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | |
extension Box: ObservableObject { | |
@inlinable | |
public var publisher: AnyPublisher<Content, Never> { objectWillChange | |
.map { self.wrappedValue } | |
.eraseToAnyPublisher() | |
} | |
} | |
#endif | |
@propertyWrapper | |
@dynamicMemberLookup | |
public final class Box<Content> { | |
#if canImport(Combine) | |
public var content: Content { | |
willSet { | |
if #available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) { | |
self.objectWillChange.send() | |
} | |
} | |
} | |
#else | |
public var content: Content | |
#endif | |
@inlinable | |
public var wrappedValue: Content { | |
get { content } | |
set { content = newValue } | |
} | |
@inlinable | |
public convenience init(_ wrappedValue: Content) { | |
self.init(wrappedValue: wrappedValue) | |
} | |
@inlinable | |
public init(wrappedValue: Content) { | |
self.content = wrappedValue | |
} | |
@inlinable | |
public var projectedValue: Ref<Content> { ref } | |
@inlinable | |
public var ref: Ref<Content> { | |
Ref<Content>( | |
read: { self.wrappedValue }, | |
write: { self.wrappedValue = $0 } | |
) | |
} | |
@inlinable | |
public subscript<U>(dynamicMember keyPath: KeyPath<Content, U>) -> U { | |
get { self.wrappedValue[keyPath: keyPath] } | |
} | |
@inlinable | |
public subscript<U>(dynamicMember keyPath: WritableKeyPath<Content, U>) -> U { | |
get { self.wrappedValue[keyPath: keyPath] } | |
set { self.wrappedValue[keyPath: keyPath] = newValue } | |
} | |
@inlinable | |
public subscript<U>(dynamicMember keyPath: ReferenceWritableKeyPath<Content, U>) -> U { | |
get { self.wrappedValue[keyPath: keyPath] } | |
set { self.wrappedValue[keyPath: keyPath] = newValue } | |
} | |
} | |
#if canImport(SwiftUI) | |
import SwiftUI | |
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | |
extension Ref { | |
@inlinable | |
public var binding: Binding<Value> { .init(get: read, set: write) } | |
} | |
#endif | |
@propertyWrapper | |
@dynamicMemberLookup | |
public struct Ref<Value> { | |
public let read: () -> Value | |
public let write: (Value) -> Void | |
@inlinable | |
public var value: Value { | |
get { wrappedValue } | |
nonmutating set { wrappedValue = newValue } | |
} | |
@inlinable | |
public var wrappedValue: Value { | |
get { return read() } | |
nonmutating set { write(newValue) } | |
} | |
@inlinable | |
public init(read: @escaping () -> Value, write: @escaping (Value) -> Void) { | |
self.read = read | |
self.write = write | |
} | |
#if canImport(SwiftUI) | |
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | |
@inlinable | |
public var projectedValue: Binding<Value> { binding } | |
@inlinable | |
public init(_ binding: Binding<Value>) { | |
self.read = { binding.wrappedValue } | |
self.write = { binding.wrappedValue = $0 } | |
} | |
#endif | |
@inlinable | |
public subscript<U>(dynamicMember keyPath: WritableKeyPath<Value, U>) -> Ref<U> { | |
return Ref<U>( | |
read: { self.wrappedValue[keyPath: keyPath] }, | |
write: { self.wrappedValue[keyPath: keyPath] = $0 }) | |
} | |
@inlinable | |
public func or<T>(_ value: @escaping @autoclosure () -> T) -> Ref<T> | |
where Value == Optional<T> { | |
Ref<T>( | |
read: { self.read() ?? value() }, | |
write: { self.write($0) } | |
) | |
} | |
@inlinable | |
public func unwrap<U, T>( | |
_ keyPath: WritableKeyPath<U, T>, | |
or value: @escaping @autoclosure () -> T | |
) -> Ref<T> where Value == Optional<U> { | |
Ref<T>( | |
read: { | |
guard let object = self.read() else { return value() } | |
return object[keyPath: keyPath] | |
}, write: { value in | |
guard var object = self.read() else { return } | |
object[keyPath: keyPath] = value | |
self.write(object) | |
} | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More initializers can be found here
See also:
Back to index