Skip to content

Instantly share code, notes, and snippets.

@maximkrouk
Last active June 29, 2020 15:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maximkrouk/7b7f7a8882e5f58518db84b0a3f29ea5 to your computer and use it in GitHub Desktop.
Save maximkrouk/7b7f7a8882e5f58518db84b0a3f29ea5 to your computer and use it in GitHub Desktop.
Wrappers for value & reference types
// 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)
}
)
}
}
@maximkrouk
Copy link
Author

maximkrouk commented May 30, 2020

More initializers can be found here
See also:

Back to index

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment