简体   繁体   中英

Generic Codable types

I've got an idea that I'm trying to test out, I want to be able to have an array of different objects that are all Codable.

Here is the json

{
  "cells": 
  [
    { 
      "header": "dummy header"
    },
    {
      "title": "dummy title"
    }
  ]
}

Also a picture from Firestore because I'm not sure if I wrote that json out correctly: Firestore 模式

Here's what I had so far testing with generics

struct Submission<Cell: Codable>: Codable {
    let cells: [Cell]
}

struct ChecklistCell: Codable {
    let header: String
}

struct SegmentedCell: Codable {
    let title: String
}

The overarching goal is to decode a document that has an array (of cells) that can be different types, but are all codable. I'm not sure if this is possible, or if there is an even better approach. Thanks.

Update: 更新 I did @Fogmeister 's solution and got it working, but not the most desirable outcome. It adds a weird layer to the json that ideally wouldn't be there. Any ideas?

I have done something similar to this in the past. Not with Firestore (although, more recently I did) but with our CMS that we use.

As @vadian pointed out, heterogeneous arrays are not supported by Swift.

Also... something else to point out.

When you have a generic type defined like...

struct Submission<Cell> {
  let cells: [Cell]
}

Then, by definition, cells is a homogeneous array of a single type. If you try to put different types into it it will not compile.

You can get around this though by using an enum to bundle all your different Cells into a single type.

enum CellTypes {
  case checkList(CheckListCell)
  case segmented(SegmentedCell)
}

Now your array would be a homogeneous array of [CellTypes] where each element would be a case of the enum which would then contain the model of the cell inside it.

struct Submission {
  let cells: [CellTypes]
}

This takes some custom decoding to get straight from JSON but I can't add that right now. If you need some guidance on that I'll update the answer.

Encoding and Decoding

Something to note from a JSON point of view. Your app will need to know which type of cell is being encoded/decoded. So your original JSON schema will need some updating to add this.

The automatic update from Firestore that you have shown is a fairly common way of doing this...

The JSON looks a bit like this...

{
  "cells": 
  [
    {
      "checkListCell": { 
        "header": "dummy header"
      }
    },
    {
      "segmentedCell": {
        "title": "dummy title"
      }
    }
  ]
}

Essentially, each item in the array is now an object that has a single key. From checkListCell , segmentedCell . This will be from any of the cases of your enum. This key tells your app which type of cell the object is.

Then the object shown against that key is then the underlying cell itself.

This is probably the cleanest way of modelling this data.

So, you might have two checklist cells and then a segmented cell and finally another checklist cell.

This will look like...

{
  "cells": 
  [
    {
      "checkListCell": { 
        "header": "First checklist"
      }
    },
    {
      "checkListCell": { 
        "header": "Second checklist"
      }
    },
    {
      "segmentedCell": {
        "title": "Some segmented stuff"
      }
    },
    {
      "checkListCell": { 
        "header": "Another checklist"
      }
    },
  ]
}

The important thing to think when analysing this JSON is not that it's harder for you (as a human being) to read. But that it's required, and actually fairly easy, for your app to read and decode/encode.

Hope that makes sense.

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