Подтвердить что ты не робот

Как сохранить массив объектов в NSUserDefault с быстрым?

это класс Place, который я определил:

class Place: NSObject {

    var latitude: Double
    var longitude: Double

    init(lat: Double, lng: Double, name: String){
        self.latitude = lat
        self.longitude = lng
    }

    required init(coder aDecoder: NSCoder) {
        self.latitude = aDecoder.decodeDoubleForKey("latitude")
        self.longitude = aDecoder.decodeDoubleForKey("longitude")
    }

    func encodeWithCoder(aCoder: NSCoder!) {
        aCoder.encodeObject(latitude, forKey: "latitude")
        aCoder.encodeObject(longitude, forKey: "longitude")
    }

}

Вот как я попытался сохранить массив Place s:

var placesArray = [Place]

//...

func savePlaces() {
    NSUserDefaults.standardUserDefaults().setObject(placesArray, forKey: "places")
    println("place saved")
}

Это не сработало, вот что я получаю на консоли:

Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')

Я новичок в iOS, не могли бы вы мне помочь?

ВТОРОЕ ИЗДАНИЕ

Я нашел решение для сохранения данных:

func savePlaces(){
    let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
   NSUserDefaults.standardUserDefaults().setObject(myData, forKey: "places")
    println("place saved")
}

Но я получаю сообщение об ошибке при загрузке данных с помощью этого кода:

 let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData

 if placesData != nil {
      placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData!) as [Place]
 }

ошибка:

[NSKeyedUnarchiver decodeDoubleForKey:]: value for key (latitude) is not a double number'

Я уверен, что я заархивировал Double, есть проблема с процессом сохранения/загрузки

Любая подсказка?

4b9b3361

Ответ 1

В Руководство по программированию списка свойств:

Если объект списка свойств является контейнером (т.е. массивом или словарем), все объекты, содержащиеся в нем, также должны быть объектами списка свойств. Если массив или словарь содержит объекты, которые не являются объектами списка свойств, то вы не можете сохранять и восстанавливать иерархию данных, используя различные методы и функции свойств-списка.

Вам нужно будет преобразовать объект в экземпляр NSData и из него с помощью NSKeyedArchiver и NSKeyedUnarchiver.

Например:

func savePlaces(){
    let placesArray = [Place(lat: 123, lng: 123, name: "hi")]
    let placesData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
    NSUserDefaults.standardUserDefaults().setObject(placesData, forKey: "places")
}

func loadPlaces(){
    let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData

    if let placesData = placesData {
        let placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData) as? [Place]

        if let placesArray = placesArray {
            // do something…
        }

    }
}

Ответ 2

Swift 4

Нам нужно сериализовать наш объект swift чтобы сохранить его в userDefaults.

В Swift 4 мы можем использовать протокол Codable, который упрощает нашу сериализацию и анализ JSON.

Рабочий процесс (Сохранить объект swift в UserDefaults):

  1. Подтвердите протокол Codable для класса модели (класс Place: Codable).
  2. Создать объект класса.
  3. Сериализуйте этот класс, используя класс JsonEncoder.
  4. Сохранить сериализованный (Data) объект в UserDefaults.

Рабочий процесс (получить объект swift из UserDefaults):

  1. Получить данные из UserDefaults (которые будут возвращать объект Serialized (Data))
  2. Декодирование данных с использованием класса JsonDecoder

Код Swift 4:

class Place: Codable {
    var latitude: Double
    var longitude: Double

    init(lat : Double, long: Double) {
        self.latitude = lat
        self.longitude = long
    }

    public static func savePlaces(){
        var placeArray = [Place]()
        let place1 = Place(lat: 10.0, long: 12.0)
        let place2 = Place(lat: 5.0, long: 6.7)
        let place3 = Place(lat: 4.3, long: 6.7)
        placeArray.append(place1)
        placeArray.append(place2)
        placeArray.append(place3)
        let placesData = try! JSONEncoder().encode(placeArray)
        UserDefaults.standard.set(placesData, forKey: "places")
    }

    public static func getPlaces() -> [Place]?{
        let placeData = UserDefaults.standard.data(forKey: "places")
        let placeArray = try! JSONDecoder().decode([Place].self, from: placeData!)
        return placeArray
    }
}

Ответ 3

Swift 3 и 4

Ниже приведен полный пример кода в Swift 3 и 4.

import Foundation

class Place: NSObject, NSCoding {

    var latitude: Double
    var longitude: Double
    var name: String

    init(latitude: Double, longitude: Double, name: String) {
        self.latitude = latitude
        self.longitude = longitude
        self.name = name
    }

    required init?(coder aDecoder: NSCoder) {
        self.latitude = aDecoder.decodeDouble(forKey: "latitude")
        self.longitude = aDecoder.decodeDouble(forKey: "longitude")
        self.name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(latitude, forKey: "latitude")
        aCoder.encode(longitude, forKey: "longitude")
        aCoder.encode(name, forKey: "name")
    }
}

func savePlaces() {
    var placesArray: [Place] = []
    placesArray.append(Place(latitude: 12, longitude: 21, name: "place 1"))
    placesArray.append(Place(latitude: 23, longitude: 32, name: "place 2"))
    placesArray.append(Place(latitude: 34, longitude: 43, name: "place 3"))

    let placesData = NSKeyedArchiver.archivedData(withRootObject: placesArray)
    UserDefaults.standard.set(placesData, forKey: "places")
}

func loadPlaces() {
    guard let placesData = UserDefaults.standard.object(forKey: "places") as? NSData else {
        print("'places' not found in UserDefaults")
        return
    }

    guard let placesArray = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as? [Place] else {
        print("Could not unarchive from placesData")
        return
    }

    for place in placesArray {
        print("")
        print("place.latitude: \(place.latitude)")
        print("place.longitude: \(place.longitude)")
        print("place.name: \(place.name)")
    }
}

 

Пример использования:

savePlaces()
loadPlaces()

 

Выход консоли:

place.latitude: 12.0
place.longitude: 21.0
place.name: 'place 1'

place.latitude: 23.0
place.longitude: 32.0
place.name: 'place 2'

place.latitude: 34.0
place.longitude: 43.0
place.name: 'place 3'

Ответ 4

В Swift 4. 0+ мы можем использовать псевдоним типа Codable, который состоит из 2 протоколов: Decodable & Кодируемый.

Для удобства я создал универсальные методы декодирования и кодирования, тип которых ограничен Codable:

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()
    }
}

Использование - сохранение объекта/массива/словаря:

Допустим, у нас есть собственный объект:

struct MyObject: Codable {
    let counter: Int
    let message: String
}

и мы создали экземпляр из него:

let myObjectInstance = MyObject(counter: 10, message: "hi there")

Используя общее расширение, описанное выше, теперь мы можем сохранить этот объект следующим образом:

UserDefaults.standard.encode(for: myObjectInstance, using: String(describing: MyObject.self))

Сохранение массива того же типа:

UserDefaults.standard.encode(for:[myFirstObjectInstance, mySecondObjectInstance], using: String(describing: MyObject.self))

Сохранение словаря с таким типом:

let dictionary = ["HashMe" : myObjectInstance]
UserDefaults.standard.encode(for: dictionary, using: String(describing: MyObject.self))

Использование - загрузка объекта/массива/словаря:

Загрузка одного объекта:

let myDecodedObject = UserDefaults.standard.decode(for: MyObject.self, using: String(describing: MyObject.self))

Загрузка массива того же типа:

let myDecodedObject = UserDefaults.standard.decode(for: [MyObject].self, using: String(describing: MyObject.self))

Загрузка словаря с таким типом:

let myDecodedObject = UserDefaults.standard.decode(for: ["HashMe" : myObjectInstance].self, using: String(describing: MyObject.self))

Ответ 5

Вы создали место для архивации, но теперь вы предполагаете, что массив Place будет автоматически архивироваться при сохранении в NSUserDefaults. Этого не будет. Вы должны архивировать его. Сообщение об ошибке сообщает об этом. Единственными вещами, которые могут быть сохранены в NSUserDefaults, являются объекты со списками свойств: NSArray, NSDictionary, NSString, NSData, NSDate и NSNumber. Объект Place не является одним из них.

Вместо того, чтобы пытаться сохранить массив напрямую, запишите его. Теперь это NSData - и это один из типов объектов списка свойств:

let myData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)

Теперь вы можете хранить myData в NSUserDefaults, потому что это NSData. Конечно, когда вы вытащите его снова, вам также придется его разблокировать, чтобы превратить его из NSData обратно в массив Place.

РЕДАКТИРОВАТЬ. Кстати, мне кажется, что, как запоздалая мысль, ваш класс Place должен явно принять протокол NSCoding, чтобы заставить это работать. Вы, кажется, пропустили этот шаг.

Ответ 7

Ниже приведен код, который я использую. Надеюсь, это поможет.

Считайте, что Спорт является объектом. Сначала нам нужно подтвердить класс Codable для класса объекта, как показано ниже.

class Sport: Codable {
    var id: Int!
    var name: String!
}

Тогда мы могли бы просто использовать следующий код для сохранения массива объекта с помощью UserDefaults.

func saveSports(_ list:[Sport]) {
        UserDefaults.standard..set(try? PropertyListEncoder().encode(list), forKey:"KEY")
        UserDefaults.standard..synchronize()
    }

Теперь мы можем просто передать список объектов в качестве параметра и сохранить с помощью метода saveSports()

Для извлечения сохраненных данных мы могли бы использовать следующий код.

func getSports() -> [Sport]? {
    if let data = UserDefaults.standard.value(forKey:"KEY") as? Data {
        let decodedSports = try? PropertyListDecoder().decode([Sport].self, from: data)
        return decodedSports
    }
    return nil
}

Метод getSports() возвращает сохраненные данные.