Swift - сохранить массив [String: Any] в NSUserDefauls

Я обнаружил, что это расширение использует Codable для сохранения в 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()
    }
}

Но как я вижу у меня ошибка Type 'OfflineRequest' does not conform to protocol 'Decodable' похоже из-за Any.

У меня есть следующая структура, которую я хочу сохранить:

struct OfflineRequest: Codable {
    let url: String
    let params: [String: Any]
}

Идея состоит в том, чтобы постоянно хранить стек (массив) запросов, которые не увенчались успехом из-за каких-либо проблем с подключением. Итак, у меня есть модель данных Core Data, и я преобразовываю ее свойства в [String: Any] перед отправкой на сервер. Но теперь я хочу создать автономный первый алгоритм. Поэтому, если пользователь не в сети, я хочу, чтобы URL-адрес и параметры постоянного хранилища были [String: Any]. Как правильно обращаться с does not conform to protocol 'Decodable' в этом случае?


person Matrosov Alexander    schedule 30.07.2019    source источник
comment
почему вы не храните только строку формата json и не используете ее для кодирования и декодирования?   -  person m1sh0    schedule 30.07.2019
comment
Если вы уже используете Core Data, почему бы вам не использовать его для хранения ваших запросов вместо того, чтобы злоупотреблять настройками пользователя по умолчанию?   -  person Paulw11    schedule 30.07.2019
comment
@ Paulw11, спасибо за комментарий, Пол, но это очень просто объяснить. Итак, у меня есть стек или массив запросов в очереди, которые на самом деле являются всеми моими операциями, которые я хотел отправить на сервер, но они не увенчались успехом. Поэтому, если я использую основные объекты данных с флагом isSync или любым другим, я все еще могу изменить исходную запись соответствующего объекта, пока я не в сети, и когда соединение будет восстановлено, я отправлю измененный объект в случае каких-либо изменений с ним, я даже могу удалить некоторые запись, поэтому мне нужен сложный алгоритм, который ищет мои зависимости, которые еще не синхронизированы, а также обновляет их. Правильно?   -  person Matrosov Alexander    schedule 30.07.2019
comment
Я понимаю, что вы пытаетесь сделать. Я предлагал хранить незавершенные операции в объекте другого типа в Core Data, а не в User Defaults.   -  person Paulw11    schedule 30.07.2019
comment
@ Paulw11 о, хорошо, понял! Благодарность!   -  person Matrosov Alexander    schedule 31.07.2019


Ответы (2)


Вы можете использовать JSONSerialization.data(withJSONObject: Any) для кодирования словаря и JSONSerialization.JSONObject(with: Data) для его декодирования. Вам просто нужно убедиться, что вы передаете действительный объект json (в данном случае словарь), если вы передаете словарь с такими типами, как Date, он выйдет из строя. Попробуйте так:

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"
person Leo Dabus    schedule 30.07.2019

Вы получаете сообщение об ошибке из-за [String:Any]. Я предлагаю вам преобразовать [String:Any] в Data с помощью JSONSerialization.data.

И тогда вы можете использовать вот так

struct OfflineRequest: Codable {
    let url: String
    let params: Data


    enum CodingKeys: String, CodingKey {
        case url
        case params

    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        params = try values.decode(Data.self, forKey: .params)
        url = try values.decode(String.self, forKey: .url)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(url, forKey: .url)
        try container.encode(params, forKey: .params)
    }
}
person Prashant Tukadiya    schedule 30.07.2019