Reputation: 586
I am trying to save an array of custom objects to UserDefaults. I believe I should, at this point, be saving correctly, but I continue to get the "unrecognized selector" error. I think this may be because two parts of my custom object are arrays of other custom objects. What am I doing wrong here?
The Object:
class SaleObject: NSObject, NSCoding {
//MARK: Properties
var ranges: [RangeObject]
var timberSaleName: String
var lbOperatorName: String
var lbOperatorID: String
var timberSaleID: Int
var ID: Int
var contracts: [ContractObject]
//MARK: Initialization
init(ranges: [RangeObject], timberSaleName: String, lbOperatorName: String, lbOperatorID: String, timberSaleID: Int, ID: Int ,contracts: [ContractObject]) {
//Initialize stored properties
self.ranges = ranges
self.timberSaleName = timberSaleName
self.lbOperatorName = lbOperatorName
self.lbOperatorID = lbOperatorID
self.timberSaleID = timberSaleID
self.ID = ID
self.contracts = contracts
}
required init?(coder aDecoder: NSCoder) {
self.ranges = (aDecoder.decodeObject(forKey: "ranges") as? [RangeObject])!
self.timberSaleName = (aDecoder.decodeObject(forKey: "timber_sale_name") as? String)!
self.lbOperatorName = (aDecoder.decodeObject(forKey: "lb_operator_name") as? String)!
self.lbOperatorID = (aDecoder.decodeObject(forKey: "lb_operator_id") as? String)!
self.timberSaleID = (aDecoder.decodeObject(forKey: "timber_sale_id") as? Int)!
self.ID = (aDecoder.decodeObject(forKey: "id") as? Int)!
self.contracts = (aDecoder.decodeObject(forKey: "contracts") as? [ContractObject])!
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.ranges, forKey: "ranges")
aCoder.encode(self.timberSaleName, forKey: "timber_sale_name")
aCoder.encode(self.lbOperatorName, forKey: "lb_operator_name")
aCoder.encode(self.lbOperatorID, forKey: "lb_operator_id")
aCoder.encode(self.timberSaleID, forKey: "timber_sale_id")
aCoder.encode(self.ID, forKey: "id")
aCoder.encode(self.contracts, forKey: "contracts")
}
}
The save attempt:
let userDefaults = UserDefaults.standard
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: self.saleArray)
userDefaults.set(encodedData, forKey: "sales")
The error:
2018-11-28 14:28:30.024768-0600 Load BOSS[14991:282335]
libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not
supported on this platform.
2018-11-28 14:28:30.362954-0600 Load BOSS[14991:282335] [MC] System
group container for systemgroup.com.apple.configurationprofiles path
is /Users/beauburchfield/Library/Developer/
CoreSimulator/Devices/4ED03814-DD8C-43F3-ABA7-
B5D0751161A0/data/Containers/Shared/SystemGroup/
systemgroup.com.apple.configurationprofiles
2018-11-28 14:28:30.364591-0600 Load BOSS[14991:282335] [MC] Reading
from private effective user settings.
2018-11-28 14:28:31.034136-0600 Load BOSS[14991:282394] -
[Load_BOSS.RangeObject encodeWithCoder:]: unrecognized selector sent
to
instance 0x600001bcc680
2018-11-28 14:28:31.037429-0600 Load BOSS[14991:282394] ***
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[Load_BOSS.RangeObject
encodeWithCoder:]: unrecognized selector sent to instance
0x600001bcc680'
*** First throw call stack:
(
0 CoreFoundation 0x000000010cbc51bb _ .
_exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010b73b735
objc_exception_throw + 48
2 CoreFoundation 0x000000010cbe3f44 -
[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x000000010cbc9ed6 ___forwarding___ + 1446
4 CoreFoundation 0x000000010cbcbda8 _CF_forwarding_prep_0 + 120
5 Foundation 0x000000010b182175 _encodeObject + 1230
6 Foundation 0x000000010b182aee -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 439
7 Foundation 0x000000010b182175 _encodeObject + 1230
8 Load BOSS 0x000000010add0e2f $S9Load_BOSS10SaleObjectC6encode4withySo7NSCoderC_tF + 207
9 Load BOSS 0x000000010add128c $S9Load_BOSS10SaleObjectC6encode4withySo7NSCoderC_tFTo + 60
10 Foundation 0x000000010b182175 _encodeObject + 1230
11 Foundation 0x000000010b182aee -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 439
12 Foundation 0x000000010b182175 _encodeObject + 1230
13 Foundation 0x000000010b18122e +[NSKeyedArchiver archivedDataWithRootObject:] + 156
14 Load BOSS 0x000000010adbf15a $S9Load_BOSS18MainViewControllerC14getJsonFromUrlyyFy10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_ + 15706
15 Load BOSS 0x000000010adbf43d $S9Load_BOSS18MainViewControllerC14getJsonFromUrlyyFy10Foundation4DataVSg_So13NSURLResponseCSgs5Error_pSgtcfU_TA + 13
16 Load BOSS 0x000000010adbf590 $S10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 336
17 CFNetwork 0x000000010e0d9940 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
18 CFNetwork 0x000000010e0efb0c __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 172
19 Foundation 0x000000010b1a8f9e __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
20 Foundation 0x000000010b1a8ea5 -[NSBlockOperation main] + 68
21 Foundation 0x000000010b1a5c14 -[__NSOperationInternal _start:] + 689
22 Foundation 0x000000010b1abc4b __NSOQSchedule_f + 227
23 libdispatch.dylib 0x000000010f0ae595 _dispatch_call_block_and_release + 12
24 libdispatch.dylib 0x000000010f0af602 _dispatch_client_callout + 8
25 libdispatch.dylib 0x000000010f0b254d _dispatch_continuation_pop + 565
26 libdispatch.dylib 0x000000010f0b1927 _dispatch_async_redirect_invoke + 859
27 libdispatch.dylib 0x000000010f0c000a _dispatch_root_queue_drain + 351
28 libdispatch.dylib 0x000000010f0c09af _dispatch_worker_thread2 + 130
29 libsystem_pthread.dylib 0x000000010f49e70e _pthread_wqthread + 619
30 libsystem_pthread.dylib 0x000000010f49e435 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Upvotes: 0
Views: 2331
Reputation: 26
Go ahead and implement NSCoding for the other two custom objects. Also, change your decodeObject to decodeInteger on all Integers of your custom objects, and remove the "as! Int" from them. Then, do this:
let userDefaults = UserDefaults.standard
let encodedData = NSKeyedArchiver.archivedData(withRootObject: self.saleArray)
userDefaults.set(encodedData, forKey: "sales")
To retrieve the data, do this:
let newArray = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as! [SaleObject]
After you have it working, go back and research Codable. Enjoy!
Upvotes: 1
Reputation: 100523
The crash here
[Load_BOSS.RangeObject encodeWithCoder:]: unrecognized selector sent
gives the reason which is making the root class conform to
class SaleObject: NSObject, NSCoding {
doesn't mean that you can use custom object ( RangeObject
in your case ) without making it also to conform like
class RangeObject : NSObject, NSCoding {
Highly recommend using Codable
class SaleObject: Codable {
let ranges: [RangeObject]
let timberSaleName: String
let lbOperatorName: String
let lbOperatorID: String
let timberSaleID: Int
let ID: Int
let contracts: [ContractObject]
}
class RangeObject : Codable {
// add it's properties
}
How to use:
let data = try? JSONEncoder().encode(obj) // obj may be single SaleObject or array
let obj = try? JSONDecoder().decode(SaleObject.self,from:data) // for data being array make it [SaleObject].self
Upvotes: 2
Reputation: 1210
NSCoding must be implemented for your two custom objects as well. Everything else is fine.
There is one thing I want to mention, though.
(aDecoder.decodeObject(forKey: "timber_sale_name") as? String)!
can be written as
aDecoder.decodeObject(forKey: "timber_sale_name") as! String
It basically means the same but is easier to read.
Upvotes: 1