2

I am trying to store the dictionary in my class Marker but it is throwing an error saying it is not encodable or decodable. I can see the error is caused by the [String: Any] but how can I go around it?

var buttonActions : [String: [String: [String:Any]]] = [:]

Save and Load

func saveData() {
    let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("\(fileName).plist")

    let encoder = PropertyListEncoder()
    do {
        let data = try encoder.encode(markerArray)
        try data.write(to: dataFilePath!)
        print("Saved")
    } catch {
        print("Error Encoding \(error)")
    }
}

func loadData() {
    let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("\(fileName).plist")

    if let data = try? Data(contentsOf: dataFilePath!){
        let decoder = PropertyListDecoder()
        do {
            markerArray = try decoder.decode([Marker].self, from: data)
        } catch {
            print("Decode Error \(error)")
        }
    }

Class

class Marker : Encodable, Decodable {
    var UUIDpic: UUID = UUID()
    var alpha: Int = 1
    var buttonType: Int = 0
    var buttonActions : [String: [String: [String:Any]]] = [:]
    var buttonNameColor: String = ""
    var buttonNameFontSize: Int = 10
    var buttonShape: String = ""
    var loggerRect: String = ""
    var maskColor: String = ""
    var name: String = ""
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
STerrier
  • 3,755
  • 1
  • 16
  • 41

2 Answers2

7

Unfortunately you cannot use encode or decode on generic types containing Any (e.g. [String: Any] or [Any]). Any does not conform to protocols Encodable nor Decodable and Swift doesn't know how to encode/decode it. You must use a concrete generic type for your dictionary (e.g. [String: String]).

If you still need to use a general type like Any you have to implement encode(to:) and init(from:) methods. Another option would be to use a struct instead of your [String: [String: [String:Any]]] which conforms to Codable (Encodable & Decodable). You will still have to implement encode(to:) and init(from:) methods in that struct, but the bright side is that you will not have to write the encoder.encode() story for all the properties like you would have to if you implement them in the Marker class.

  • Hi Andrada, any chance you could provide a link or an example? I changed my class to a struct and I kind of understand what you mean but I can't work out to implement it sorry. – STerrier Dec 04 '18 at 21:51
  • Have a look at this answer: https://stackoverflow.com/questions/44603248/how-to-decode-a-property-with-type-of-json-dictionary-in-swift-4-decodable-proto?fbclid=IwAR2jstxmdG4bbdIJBKRZJXzXLi5Uyk95RI3he1yWw26fKy1ZQNSha1HsJcs also, you don't need to change your class into a struct, you rather need an additional struct for that dictionary – Andrada Farcaș Dec 04 '18 at 22:35
  • Thanks Andrada, I'll try to implement it now – STerrier Dec 04 '18 at 23:36
  • I went throught the link but I didn't understand how to implement the most popular answer so I used the github linked to it that answer. https://gist.github.com/mbuchetics/5d5600c50c369bea29db4a3986cee94f This kind of work but not if I want to add an array in [String:Any]. Any ideas? thanks – STerrier Dec 06 '18 at 05:27
  • Thanks Andrada for the help. I managed to work it out because of you :) – STerrier Dec 07 '18 at 04:03
2

So finally worked it out with the help of Andrada.

I added a second struct which held the action and by passed having to use [string:any]

class Marker : Encodable, Decodable {
var UUIDpic: UUID = UUID()
var alpha: Int = 1
var buttonType: Int = 0
var buttonAction : [String: [ButtonAction]] = [:] //Dictionary I edited using the new struct
var buttonNameColor: String = ""
var buttonNameFontSize: Int = 10
var buttonShape: String = ""
var loggerRect: String = ""
var maskColor: String = ""
var name: String = ""
}

Below is the struct I added

struct ButtonAction: Codable {
var action: String
var array_linked_of_buttons: [[String:String]]

init(action: String, array_linked_of_buttons: [[String:String]]) {
 self.action = action
 self.array_linked_of_buttons = array_linked_of_buttons
    }
}

Make sure to init your struct or it won't work.

STerrier
  • 3,755
  • 1
  • 16
  • 41