Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active November 12, 2022 20:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IanKeen/f2f3f6ca6850a8b86874560e3b315e13 to your computer and use it in GitHub Desktop.
Save IanKeen/f2f3f6ca6850a8b86874560e3b315e13 to your computer and use it in GitHub Desktop.
PropertyWrapper: Add `indirect` functionality to structs (w/ COW)
struct Value {
var foo: Int
@Indirect var inner: Value?
}
let x = Value(foo: 1, inner: .init(foo: 2, inner: .init(foo: 3, inner: nil)))
x.inner?.inner?.foo // 3
@propertyWrapper
public struct Indirect<T> {
private var box: Box<T>
public var wrappedValue: T {
get { box.value }
set {
if !isKnownUniquelyReferenced(&box) {
box = Box(box.value)
} else {
box.value = newValue
}
}
}
public init(wrappedValue: T) {
self.box = Box(wrappedValue)
}
}
final class Box<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
extension Indirect: Decodable where T: Decodable {
public init(from decoder: Decoder) throws {
self.init(wrappedValue: try T(from: decoder))
}
}
extension Indirect: Encodable where T: Encodable {
public func encode(to encoder: Encoder) throws {
try self.wrappedValue.encode(to: encoder)
}
}
extension Indirect: Equatable where T: Equatable {
public static func ==(lhs: Indirect, rhs: Indirect) -> Bool {
return lhs.wrappedValue == rhs.wrappedValue
}
}
// This are needed to deal with wrapping optionals
extension KeyedDecodingContainer {
public func decode<T: Decodable>(_ type: Indirect<T?>.Type, forKey key: KeyedDecodingContainer.Key) throws -> Indirect<T?> {
return Indirect<T?>(wrappedValue: try decodeIfPresent(T.self, forKey: key))
}
}
extension KeyedEncodingContainer {
public mutating func encode<T: Encodable>(_ value: Indirect<T?>, forKey key: KeyedEncodingContainer<K>.Key) throws {
guard let value = value.wrappedValue else { return }
try encode(value, forKey: key)
}
}
@IanKeen
Copy link
Author

IanKeen commented Apr 16, 2020

Thanks to @woolsweater and @timvermeulen for pointing out I'd need COW here :D

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