-1

I have a struct called Station that supports the Codable protocol. Since it is a struct, it cannot support the NSCoding protocol. I would like to save an array of these Stations to the UserDefaults. I have code that does it without any problems.

func updateRecentStations(stations: [Station]) {
    let encoder = PropertyListEncoder()
    encoder.outputFormat = .xml
    let data = try! encoder.encode(stationsArray)
    UserDefaults.standard.set(data, forKey: "RecentStations")
}

func readRecentStations() -> [Station] {
    if let data = UserDefaults.standard.object(forKey: "RecentStations") as? Data {
        let stations: [Station] = try! PropertyListDecoder().decode( [Station].self, from: data)
        return stations
    }
}

These both work. The roundtrip is happening without a problem. But if I examine the <<app>>.plist in Xcode, or call print(Array(UserDefaults.standard.dictionaryRepresentation())) the displayed data is illegible. The Data that has been written looks like it is stored in a hexadecimal format. That said, it I open the <<app>>.plist within a text editor, I can see the xml within there, but the .plist isn't meant to be read that way.

The UserDefaults does not appear to have support for writing a plist to the .plist. Everything works, but because the plist editor can't read what I've written, I feel I am not doing things in the proper way.

Is there a way to support a Codable being written to the UserDefaults in such a way that it maintains its structure in a human readable way within there? To be clear, I am looking for a way that replicates the way NSCoding would add to the NSUserDefaults hierarchy.

Erik Allen
  • 1,841
  • 2
  • 20
  • 36

1 Answers1

0

You are storing Data in UserDefaults. That will get encoded as a base64 string which is why it isn't human readable.

Since you are encoding to an XML format, the resulting Data will actually be the data of a string representing the XML. So create a String from data. Then store that string in UserDefaults. Then the XML will be readable when viewing the UserDefaults plist file.

Note: the following hasn't been tested. There may be typos.

func updateRecentStations(stations: [Station]) {
    let encoder = PropertyListEncoder()
    encoder.outputFormat = .xml
    let data = try! encoder.encode(stationsArray)
    let xml = String(data: data, encoding: .utf8)!
    UserDefaults.standard.set(xml, forKey: "RecentStations")
}

func readRecentStations() -> [Station] {
    if let xml = UserDefaults.standard.object(forKey: "RecentStations") as? String {
        let data = xml.data(using: .utf8)!
        let stations: [Station] = try! PropertyListDecoder().decode( [Station].self, from: data)
        return stations
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Although that will generally solve the issue as I initially described it isn't what I am looking for. I've updated the question to be clearer: I am looking for a way that replicates the way `NSCoding` would add to the `NSUserDefaults` hierarchy. – Erik Allen Jul 24 '19 at 20:20
  • Update your question to include an example of the output you want. – rmaddy Jul 24 '19 at 20:23