I have found this extension to use Codable to be saved into NSUserDefaults
extension UserDefaults {
func decode<T : Codable>(for type : T.Type, using key : String) -> T? {
let defaults = UserDefaults.standard
guard let data = defaults.object(forKey: key) as? Data else {return nil}
let decodedObject = try? PropertyListDecoder().decode(type, from: data)
return decodedObject
}
func encode<T : Codable>(for type : T, using key : String) {
let defaults = UserDefaults.standard
let encodedData = try? PropertyListEncoder().encode(type)
defaults.set(encodedData, forKey: key)
defaults.synchronize()
}
}
But as I see I have an error Type 'OfflineRequest' does not conform to protocol 'Decodable'
looks like because of Any
.
I have next structure I want to save:
struct OfflineRequest: Codable {
let url: String
let params: [String: Any]
}
The idea is to persistence store stack (array) of requests which are unsuccessful because of any connection issues. So I have Core Data data model and I am converting its properties to [String: Any] before sending it to the server. But now I want to create offline first algorithm. So in case user is offline I want to persistent store url and params which is [String: Any]. How to handle does not conform to protocol 'Decodable'
correctly in this case?
You can use JSONSerialization.data(withJSONObject: Any)
to encode your dictionary and JSONSerialization.JSONObject(with: Data)
to decode it. You just need to make sure you pass a valid json object (in this case a dictionary), if you pass a dictionary with types like a Date
it will crash. Try like this:
struct OfflineRequest: Codable {
let url: String
let params: [String: Any]
}
extension OfflineRequest {
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
url = try container.decode(String.self)
params = try JSONSerialization.jsonObject(with: container.decode(Data.self)) as? [String:Any] ?? [:]
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(url)
try container.encode(JSONSerialization.data(withJSONObject: params))
}
}
let test = [OfflineRequest(url: "http://www.google.com", params: ["a":"1","b":"test", "c":["d":"test"]])]
let data = try! JSONEncoder().encode(test)
let loaded = try! JSONDecoder().decode([OfflineRequest].self, from: data)
print(loaded) // [__lldb_expr_3.OfflineRequest(url: "http://www.google.com", params: ["b": test, "a": 1, "c": {\n d = test;\n}])]\n"