Skip to content

Instantly share code, notes, and snippets.

@BigZaphod
Created May 18, 2022 03:00
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save BigZaphod/1e65a03219606fb1e81cd1445518683f to your computer and use it in GitHub Desktop.
Save BigZaphod/1e65a03219606fb1e81cd1445518683f to your computer and use it in GitHub Desktop.
//
// A Swift property wrapper for adding "indirect" to struct properties.
// Enum supports this out of the box, but for some reason struct doesn't.
//
// This is useful when you want to do something recursive with structs like:
//
// struct Node {
// var next: Node?
// }
//
// Normally you can't do this. You get an error like:
// error: Value type 'Node' cannot have a stored property that recursively contains it
//
// One valid workaround is to use a class - but what if you *really* don't want to?!
// Enter the @Indirect property wrapper! Use it like so:
//
// struct Node {
// @Indirect var next: Node? // it works!
// }
//
// Tada!
//
// This works by piggybacking on the indirect support that already exists for enums.
// I discovered this here: https://forums.swift.org/t/using-indirect-modifier-for-struct-properties/37600/14
// My contribution is making it work with Codable (even when T is optional).
//
@propertyWrapper
enum Indirect<T> {
indirect case wrapped(T)
init(wrappedValue initialValue: T) {
self = .wrapped(initialValue)
}
var wrappedValue: T {
get { switch self { case .wrapped(let x): return x } }
set { self = .wrapped(newValue) }
}
}
extension Indirect: Decodable where T: Decodable {
init(from decoder: Decoder) throws {
try self.init(wrappedValue: T(from: decoder))
}
}
extension Indirect: Encodable where T: Encodable {
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
}
extension KeyedDecodingContainer {
func decode<T: Decodable>(_: Indirect<T>.Type, forKey key: Key) throws -> Indirect<T> {
return try Indirect(wrappedValue: decode(T.self, forKey: key))
}
func decode<T: Decodable>(_: Indirect<Optional<T>>.Type, forKey key: Key) throws -> Indirect<Optional<T>> {
return try Indirect(wrappedValue: decodeIfPresent(T.self, forKey: key))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment