Skip to content

Instantly share code, notes, and snippets.

@insidegui
Created January 4, 2022 18:03
Show Gist options
  • Select an option

  • Save insidegui/15a006d76bf7c238bceb221ea21f4e3f to your computer and use it in GitHub Desktop.

Select an option

Save insidegui/15a006d76bf7c238bceb221ea21f4e3f to your computer and use it in GitHub Desktop.

Revisions

  1. insidegui created this gist Jan 4, 2022.
    44 changes: 44 additions & 0 deletions CodableReference.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    protocol ReferenceEncodable: Identifiable {
    static var referenceStorageKey: CodingUserInfoKey { get }
    }

    extension ReferenceEncodable {
    static var referenceStorageKey: CodingUserInfoKey {
    CodingUserInfoKey(rawValue: String(describing: Self.self) + "ReferenceStorage")!
    }
    }

    @propertyWrapper
    struct CodableReference<T>: Hashable where T: ReferenceEncodable, T.ID: Codable & Hashable {
    var wrappedValue: T

    func hash(into hasher: inout Hasher) {
    hasher.combine(wrappedValue.id)
    }

    static func ==(lhs: Self, rhs: Self) -> Bool {
    lhs.wrappedValue.id == rhs.wrappedValue.id
    }
    }

    extension CodableReference: Codable {
    func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    try container.encode(wrappedValue.id)
    }

    init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let id = try container.decode(T.ID.self)

    guard let storage = decoder.userInfo[T.referenceStorageKey] as? [T] else {
    throw DecodingError.dataCorruptedError(in: container, debugDescription: "Couldn't find reference storage for type \(String(describing: T.self)), decoder is missing a userInfo key \(T.referenceStorageKey.rawValue) of type [\(String(describing: T.self))]")
    }

    guard let concreteValue = storage.first(where: { $0.id == id }) else {
    throw DecodingError.dataCorruptedError(in: container, debugDescription: "Couldn't find \(String(describing: T.self)) with id \(id)")
    }

    self.init(wrappedValue: concreteValue)
    }
    }