I am trying to save custom object to UserDefaults and I'm using this as a source code. It crashes immediately on the get part. This is my code:
class Settings {
static let defaults:UserDefaults = UserDefaults.standard
///VIP object:
class VIP: NSObject, NSCoding {
let email: String
let name: String
let relation: String
init(email: String, name: String, relation: String) {
self.email = email
self.name = name
self.relation = relation
}
required init(coder decoder: NSCoder) {
self.email = decoder.decodeObject(forKey: "email") as! String
self.name = decoder.decodeObject(forKey: "name") as! String
self.relation = decoder.decodeObject(forKey: "relation") as! String
}
func encode(with coder: NSCoder) {
coder.encode(email, forKey: "email")
coder.encode(name, forKey: "name")
coder.encode(relation, forKey: "relation")
}
}
///access VIPs array with this
static var VIPs: [Settings.VIP] {
get {
////CRASH (vips is not nil, and some amount of bytes)
if let vips = self.defaults.data(forKey: "VIPs"), let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: vips) as? [Settings.VIP] {
myPeopleList.forEach({print($0.email)})
return myPeopleList
} else {
return []
}
}
set {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: newValue)
self.defaults.set(encodedData, forKey: "VIPs")
}
}
}
Not sure if I am missing something, the String saving and retrieving succeeds, but not this custom object.
Slightly rewriting the get method like this:
get {
if let vips = self.defaults.data(forKey: "VIPs"){
print("counting: ", vips.count)
if let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: vips) as? [Settings.VIP]{
myPeopleList.forEach({print($0.email)})
}
}
helps me to understand that it crashes on the NSKeyedUnarchiver.unarchiveObject()
part. But the reason is unclear.
Log of the error:
Date/Time: 2017-09-12 21:00:43.4970 +0200
Launch Time: 2017-09-12 21:00:43.1623 +0200
OS Version: iPhone OS 10.3.3 (14G5037b)
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 2
Application Specific Information:
abort() called
Filtered syslog:
None found
Last Exception Backtrace:
0 CoreFoundation 0x18f0c6fe0 __exceptionPreprocess + 124
1 libobjc.A.dylib 0x18db28538 objc_exception_throw + 56
2 Foundation 0x18fb4ab0c -[NSCoder(Exceptions) __failWithException:] + 132
3 Foundation 0x18fb4acc0 -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 436
4 Foundation 0x18fb16dac _decodeObjectBinary + 408
5 Foundation 0x18fb1df10 -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1544
6 Foundation 0x18fab384c -[NSArray(NSArray) initWithCoder:] + 216
7 Foundation 0x18fb17430 _decodeObjectBinary + 2076
8 Foundation 0x18fb16b68 _decodeObject + 308
9 Foundation 0x18fb15d94 +[NSKeyedUnarchiver unarchiveObjectWithData:] + 88
10 Prep 0x100097274 0x100014000 + 537204
11 Prep 0x100078384 0x100014000 + 410500
12 Prep 0x100078bd8 0x100014000 + 412632
13 Prep 0x1000a4370 0x100014000 + 590704
14 Prep 0x1001084ac 0x100014000 + 1000620
15 Prep 0x10010b180 0x100014000 + 1012096
16 Prep 0x100044d28 0x100014000 + 199976
17 TCC 0x1912d92f8 __TCCAccessRequest_block_invoke.73 + 492
18 TCC 0x1912dc3d4 __tccd_send_block_invoke + 340
19 libxpc.dylib 0x18e1baf30 _xpc_connection_reply_callout + 80
20 libxpc.dylib 0x18e1baea0 _xpc_connection_call_reply + 40
21 libdispatch.dylib 0x18df7e9a0 _dispatch_client_callout + 16
22 libdispatch.dylib 0x18df8d0d4 _dispatch_queue_override_invoke + 644
23 libdispatch.dylib 0x18df8ea50 _dispatch_root_queue_drain + 540
24 libdispatch.dylib 0x18df8e7d0 _dispatch_worker_thread3 + 124
25 libsystem_pthread.dylib 0x18e187100 _pthread_wqthread + 1096
26 libsystem_pthread.dylib 0x18e186cac start_wqthread + 4
UPDATE:
It happens only on second run from xcode, so running first time - it works, and trying to run again - crashes with the described output. So there must be something with the storing of the object?
XCode 9 suggest me to implicitly set the encoded name for the VIP class:
Try to make following changes and repeat your tests:
I tried this locally on Swift Playground and it works well. Nothing gets crashed. But Xcode suggested me to add @objc before the VIP class name. Please make sure that we are setting the values into VIPs variable correctly.
According to https://stackoverflow.com/a/4197319/4601900 The problem is that storing custom classes as a node in the Settings plist (NSUserDefaults) is not allowed, since the data is stored in the file system rather than an object from the application. The settings app (where this data will also be visible) has no idea what a "VIP" object is.
I have faced same issue and I managed to solve it with saving data physically in file and fetching from location where it saved
I am storing 2-3 Values to dynamically as per key
I have created Generic functions which were helpful to me.
You can fetch data like
How I Save when response comes from webservice
Hope it is helpful to you