Сохранение / получение Codable Dictionary of Struct в UserDefaults не работает для меня - PullRequest
0 голосов
/ 11 октября 2019

Свифт (v 5 / 5.1) новичок здесь, испытывает трудности с Codables ... в надежде получить совет от экспертов здесь.

Хорошо, у меня есть простой словарь из структуры, где ключэто строкаЯ хочу сохранить словарь в UserDefaults (и позже получить). Здесь есть несколько похожих вопросов, но в основном они касаются вложенных структур.

Первая попытка (обработка ошибок удалена для простоты):

public struct PriceStruct:Codable {
    var myPrice: Double
    var myTime: TimeInterval
    var selected: Bool
    var direction: Int
    var myHigh, myLow: Double

    enum CodingKeys: String, CodingKey {
        case myPrice = "myPrice"
        case myTime = "myTime"
        case selected = "selected"
        case direction = "direction"
        case myHigh = "myHigh"
        case myLow = "myLow"
    }
}

var myPrices: [String: PriceStruct] = [:]

// [fill myPrices with some data...]

func savePrices() {
   // error: Attempt to set a non-property-list object
   UserDefaults.standard.set(myPrices, forKey: "prices")
}

func loadPrices() {
   // obviously this doesn't work either
   let myPrices = UserDefaults.standard.data(forKey: "prices")
}

While I assumed from the documentation, that UserDefaults is capable of storing dictionaries, it doesn't - at least for me.
Next thing I tried was using JSONEncoder like this:

    // this time with prior JSON encoding
    func savePrices() {
       // this works
       let json = try! JSONEncoder().encode(myPrices)
       UserDefaults.standard.set(json as Data, forKey: "prices")
    }

    func loadPrices() {
       // this doesn't work
       let json = UserDefaults.standard.data(forKey: "prices")
       let decoder = JSONDecoder()
       let decoded = try! decoder.decode(PriceStruct.self, from json!)
    }

К сожалению, я получаюошибка при попытке загрузить данные обратно из UserDefaults: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "myPrice", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"myPrice\", intValue: nil) (\"myPrice\").", underlyingError: nil))

Другие варианты, которые я пробовал, - преобразование закодированного JSON в строку в кодировке UTF8 и сохранение / получение этой строки:

func savePrices() {
   // this works too
   let json = try! JSONEncoder().encode(myPrices)
   UserDefaults.standard.set(String(data: json, encoding: .utf8), forKey: "prices")
}

func loadPrices() {
   // and this doesn't work either
   let json = UserDefaults.standard.string(forKey: "prices")!.data(using: .utf8)

}

ИтакСудя по поднятой ошибке, CodingKeys является корнем проблемы. Я попытался переключиться с помощью NSKeyedArchiver и NSKeyedUnarchiver`, но безуспешно.

Мне действительно интересно, есть ли простое / универсальное решение для сохранения / загрузки словаря в UserDefaults?

Все вашикомментарии и предложения приветствуются. Спасибо!

1 Ответ

0 голосов
/ 11 октября 2019

Я попытался с помощью приведенного ниже кода в моем проекте, который будет работать для меня.

Модель пользователя

public protocol UserModel: Codable, PrimaryKey {

    var id: String { get }
    var firstName: String? { get }
    var lastName: String? { get }
    var userName: String? { get }
    var emails: [String] { get }
}

public struct User: UserModel {

    public let id: String
    public let firstName: String?
    public let lastName: String?
    public let userName: String?
    public let emails: [String]

    public enum CodingKeys: String, CodingKey {
        case id = "Id"
        case firstName = "FirstName"
        case lastName = "LastName"
        case userName = "UserName"
        case emails = "Emails"
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        do {
            self.id = try container.decode(String.self, forKey: .id)
            self.firstName = try container.decodeIfPresent(String.self, forKey: .firstName)
            self.lastName = try container.decodeIfPresent(String.self, forKey: .lastName)
            self.userName = try container.decodeIfPresent(String.self, forKey: .userName)
            self.emails = try container.decodeIfPresent([String].self, forKey: .emails) ?? []
        }
        catch let error {
            debugPrint(error)
            throw error
        }
    }
}

Я сохранил в userDefault, используя следующий способ

Класс пользовательских данных

class UserData: NSObject
{

let userDefaultKey = "user_information"

var userData: User?

    func getDictionary() -> [String: Data]
    {
        var dicInfo = [String: Data]()

        do
        {
            let _userData = try JSONEncoder().encode(userData)

            dicInfo["userData_default"]  = _userData

        }catch let error{
            print("error while save data in User Default: \(error.localizedDescription)")
        }

        return dicInfo
    }
    func saveToDefault()
    {
        let userDefault = UserDefaults.standard
        userDefault.set(getDictionary(), forKey: userDefaultKey)
        userDefault.synchronize()
    }
    func loadFromDefault()
    {
        let userDefault = UserDefaults.standard
        if let dicInfo = userDefault.object(forKey: userDefaultKey) as? [String: Data]
        {
            update(dicInfo)
        }
    }
    func update(_ dictionaryInfo: [String: Data])
    {
        do
        {
            if let _userData_data = dictionaryInfo["userData_default"]
            {
                if let _userData = try? JSONDecoder().decode(User.self, from: _userData_data) {
                    userData = _userData
                }
            }

            saveToDefault()

        }catch let error{
            print("error while load From Default data in User Default: \(error.localizedDescription)")
        }
    }
}

Надеюсь, это поможет вам.

...