简体   繁体   中英

How to use Swift Decodable to map relationships from an array of UUID keys?

I have a structure where:

Factories have many Cards ;

But also, players can have many Cards (not implemented); they are the same cards.

For testing and internal integrity checks; I'd like to keep the array of cards seperate for now in its own JSON array.

So the structure I've come up with is like this:

// Example JSON
{
    "factories": [
        {
            "id": "7a24a713-c0aa-4498-....",
            "name": "Steel Factory",
            "cost": 4,
            "references": [
                { "id": "e936b897-c203-44a5-9cf0-27720f655b68" },
                { "id": "da35424a-6546-4c72-a51c-1f6abfd601a3" }
            ],
     ...
     "cards": [
          { "id": "e936b897-c203-44a5-9cf0-27720f655b68", name: "Card #1", cost: 4 }
          { "id": "da35424a-6546-4c72-a51c-1f6abfd601a3", name: "Card #2", cost: 4 }
     ] 
...
}

Note

The Factory JSON structure has no cards ; its considered to be an empty array when decoding


Use case:

Fill the factories cards array (empty at init) with the contents of the references based off the matching UUID keys <


Swift structs are:

struct Factory: Codable, Identifiable, Equatable, Hashable {
    let id: UUID
    let name: String
    let cost: Int
    var cards: [Card]? 
    internal var references: [Reference]?

    private enum CodingKeys: String, CodingKey {
        case id, name, cost, references
    }
}

struct Reference: Codable, Identifiable, Hashable, Equatable {
    var id: UUID

    private enum CodingKeys: String, CodingKey {
        case id
    }
}

struct Card: Codable, Identifiable, Hashable, Equatable {
    var id: UUID 
    var name: String
    var cost: Int

    private enum CodingKeys: String, CodingKey {
        case id, name, cost
    }
}

So, to try to do this; I've wrote out custom initWithCoder for each struct.

extension Reference {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(UUID.self, forKey: .id)
    }
}


extension Factory {

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        id = try container.decode(UUID.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        cost = try container.decode(Int.self, forKey: .cost)
        cards = try container.decodeIfPresent([Card].self, forKey: .cards) ?? [Card]()
        references = try container.decode([Reference].self, forKey: .references)
    }
}

extension Card {
   init(from decoder: Decoder) throws {
       let container = try decoder.container(keyedBy: CodingKeys.self)

       id = try container.decode(UUID.self, forKey: .id)
       name = try container.decode(String.self, forKey: .name)
       cost = try container.decode(String.self, forKey: .cost)

   }
}

I'd like to do something like this:

// Factory decoder 
references = try container.decode([Reference].self, forKey: .references)

// Find all cards that match the reference ID
// add the matched cards to this factories cards array
let matchedCards: [Card]? = references.map { $0  }
print ("matchedCards")

But I'm not sure what to map $0 against considering the Card struct isn't directly accessible via this struct.

I am aware I can just put the Cards to sit inside the factories struct as a child array.

But I'm thinking ahead, there might come a time in the future where I need a bridging struct with key references like a one to many through JSON structure.

I appreciate any feedback offered.

let referenceIds:[String] = references.map { $0.id }

let decodedCards = container.decode([Card].self, forKey: .cards)

let matchedCards[Card] = decodedCards.filter(where: { referenceIds.contains($0.id)})

self.cards = matchedCards // factory.cards

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM