2

I am trying to save data via a codable protocol. It is an array of structures with structs inside. There are strings, images and bool values and I am thinking it is one of these data types that does not conform to the protocol.

Here is a pic of all the data I must save: original

struct Checklist {

    var name: String
    var image: UIImage?
    var items: [Item] = []
}

struct Item {

    var nameOfNote: String
    var remind: Bool
    //var date: Date
}

struct alldata: Codable {

    var checklists: [Checklist] = []
}

I have tried to include the protocol in all the structs but it also produced an error. Here's a pic: tried a solution picture

vadian
  • 274,689
  • 30
  • 353
  • 361
Antony D
  • 75
  • 2
  • 9
  • Not tried, but I doubt `UIImage` is `Codable`. What I've done instead, is customised the coding and decoding process so that the `UIImage` is converted to a base64 `String` instead – MadProgrammer Nov 11 '18 at 21:34
  • I might also point out that neither `CheckList` or `Item` are `Codable`. You should also post text/code rather then images ;) – MadProgrammer Nov 11 '18 at 21:35
  • MadProgrammer is right. `UIImage` is not `Codable` compliant. – vadian Nov 11 '18 at 21:50
  • how do you think i can make UIImage codable? – Antony D Nov 11 '18 at 21:53
  • @AntonyD JSON is a text based representation of the data, it doesn't not support binary data. However, this is neither a new problem or an uncommon one. The most common solution is to convert the binary data to a base 64 (text) representation. There are plenty of examples floating around. You then need to customise the encoding and decoding process for your `struct` so you can take control over how the image is encoded and decoded, again, not a uncommon or new problem - and well supported in Swift – MadProgrammer Nov 11 '18 at 21:58

1 Answers1

4

JSON in Swift 4 is fun (repeat this at 2am in the morning ;)).

One of the first things I always do is consult a good blog, like Ultimate Guide to JSON Parsing with Swift 4. While it might not answer the "direct" question, it will provide a lot of useful information.

Another thing to do, is consult the Apple documentation. In this, a quick look at the documentation for UIImage will point out that it does not conform to Codable, so that's a problem.

Another issue is, JSON doesn't support binary data, it's a text based solution.

This means that you need to figure out away to convert the binary image data to text. Lucky for us, people have already thought about this and the most common mechanism is to use Base 64 encoding.

A quick google search will turn up any number of solutions for encoding/decoding a UIImage to/from base 64, for example Convert between UIImage and Base64 string

Now, we just need to customise the encoding and decoding process.

The first thing is to make sure all the other fields/objects/data we use are also conform to Codable...

struct Item: Codable {
    var nameOfNote: String
    var remind: Bool
    //var date: Date
}

Then we can set out customising the encoding and decoding process for the image, maybe something like...

struct Checklist: Codable {

    enum CodingKeys: String, CodingKey {
        case image
        case name
        case items
    }

    var name: String
    var image: UIImage?
    var items: [Item] = []

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        items = try container.decode([Item].self, forKey: .items)

        if let text = try container.decodeIfPresent(String.self, forKey: .image) {
            if let data = Data(base64Encoded: text) {
                image = UIImage(data: data)
            }
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        if let image = image, let data = image.pngData() {
            try container.encode(data, forKey: .image)
        }
        try container.encode(name, forKey: .name)
        try container.encode(items, forKey: .items)
    }
}

nb: This is the basic process I've used for a couple of projects, I've not tested the above myself, but it should present the basic idea you need to get started

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366