Как сохранить массив объектов в 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, есть проблема с процессом сохранения/загрузки
Любая подсказка?
Ответы
Ответ 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):
- Подтвердите протокол Codable для класса модели (класс Place: Codable).
- Создать объект класса.
- Сериализуйте этот класс, используя класс JsonEncoder.
- Сохранить сериализованный (Data) объект в UserDefaults.
Рабочий процесс (получить объект swift из UserDefaults):
- Получить данные из UserDefaults (которые будут возвращать объект Serialized (Data))
- Декодирование данных с использованием класса 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, чтобы заставить это работать. Вы, кажется, пропустили этот шаг.
Ответ 6
Я нашел эту ссылку Как сохранить массив пользовательских объектов в UserDefaults, который дает отличное представление о сохранении UserDefaults в Swift 3 (и выше). Поделиться здесь для тех, кому это может быть полезно.
Удачного кодирования!
Ответ 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() возвращает сохраненные данные.