0

I've got a really weird error while running my app on Xcode 7 (Swift 2) that shows a "Thread 1: signal SIGABRT" running error message in the App Delegate class of my app. However I've actually already got this "Thread 1: signal SIGABRT" running error message in the App Delegate class lots of times, mainly when deleting an outlet reference in my code and forgetting to also delete it from storyboard. But that's certainly the first time I've got this same error when trying to make the command:

let wasteGain = WastesGainsClass(value: enteredMoney, originOrCat: segControlArray[segControl.selectedSegmentIndex], specification: plusEspecTField.text!, date: dateArray, mode: "gain")

gains.append(wasteGain)

NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains")

What happens is that if I just comment the line NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains") the app doesn't crash! So the error might just be in that line.

If anyone could help me, I`d thank you so much.

PS: WastesGainsClass format is like this:

class WastesGainsClass {

    var value:Int = 0
    var origin:String
    var specification:String
    var date:[String]
    var mode:String
    var rowMode:Int = 0

    init(value:Int, originOrCat:String, specification:String, date:[String], mode:String) {

        self.value = value
        self.origin = originOrCat
        self.specification = specification
        self.date = date
        self.mode = mode

    }

}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Nicolas Fonteyne
  • 164
  • 3
  • 13
  • Only property list compliant types (`NSNumber`, `NSString`, `NSDate`, `NSData`) and the collection types `NSArray` and `NSDictionary` can be saved with `NSUserDefaults`. The collection types must contain also property list compliant types. For custom classes your class must conform to `NSCoding` to be archived with `NSKeyedArchiver/Unarchiver` – vadian Sep 26 '15 at 15:55
  • @vadian You should add that as an answer; that's exactly what the problem/solution is. – Stephen Darlington Sep 26 '15 at 15:58
  • possible duplicate of [Attempt to set a non-property-list object as an NSUserDefaults](http://stackoverflow.com/questions/19720611/attempt-to-set-a-non-property-list-object-as-an-nsuserdefaults) – Glenn Sep 26 '15 at 16:05

3 Answers3

1

NSUserDefaults unfortunately can't accept arbitrary objects, only objects that can be encoded in a Property List. See Apple's reference guide for Property Lists to learn which objects can be stored.

If you need to save several WastesGainsClass objects, you may wish to write a method that returns a Dictionary encoding their Property List-representable properties, and an initializer that accepts such a Dictionary to restore the object.

However, if you truly need to save multiple custom objects like this, you probably don't want to use NSUserDefaults at all. Consider a document-based app, and look into NSCoding.

andyvn22
  • 14,696
  • 1
  • 52
  • 74
1

From documentation:

The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.

In Swift you can also use:

  • Int, UInt, Double, Float and Bool types because they are automatically bridged to NSNumber;
  • String bridged to NSString
  • [AnyObject] because it is bridged to NSArray;
  • [NSObject: AnyObject] because it is bridged to NSDictionary.

Of course type of array elements and dictionary values must be one of above types. Dictionary key type must be NSString (or bridged String).

To store instances of any other class you have two options:

  1. Your custom class must be subclass of NSObject and conform to NSCoding protocol and then you can archive object of this class to NSData with NSKeyedArchiver.archivedDataWithRootObject() and save it to NSUserDefaults and later retrieve it from NSUserDefaults and unarchive with NSKeyedUnarchiver.unarchiveObjectWithData():

    import Foundation
    
    class WastesGainsClass: NSObject, NSCoding {
        var value: Int
    
        init(value: Int) {
            self.value = value
        }
    
        required init(coder aDecoder: NSCoder) {
            value = aDecoder.decodeObjectForKey("value") as! Int
        }
    
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(value, forKey: "value")
        }
    }
    
    var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
    NSUserDefaults.standardUserDefaults().setObject(gains.map {  NSKeyedArchiver.archivedDataWithRootObject($0) }, forKey: "gains")
    
    if let gainsData = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [NSData] {
        gains = gainsData.map { NSKeyedUnarchiver.unarchiveObjectWithData($0) as! WastesGainsClass }
    }
    
  2. You can save your custom object properties to dictionary and store that dictionary in NSUserDefaults:

    import Foundation
    
    class WastesGainsClass {
        var value: Int
    
        init(value: Int) {
            self.value = value
        }
    }
    
    extension WastesGainsClass {
        convenience init(dict: [NSObject: AnyObject]) {
            self.init(value: dict["value"] as? Int ?? 0)
        }
    
        func toDict() -> [NSObject: AnyObject] {
            var d = [NSObject: AnyObject]()
            d["value"] = value
            return d
        }
    }
    
    var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
    
    NSUserDefaults.standardUserDefaults().setObject(gains.map { $0.toDict() }, forKey: "gains")
    
    if let dicts = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [[NSObject: AnyObject]] {
        gains = dicts.map { WastesGainsClass(dict: $0) }
    }
    
mixel
  • 25,177
  • 13
  • 126
  • 165
0

The code you posted tries to save an array of custom objects to NSUserDefaults. You can't do that. Implementing the NSCoding methods doesn't help. You can only store things like NSArray, NSDictionary, NSString, NSData, NSNumber, and NSDate in NSUserDefaults.

You need to convert the object to NSData (like you have in some of the code) and store that NSData in NSUserDefaults. You can even store an NSArray of NSData if you need to.

see this post : Attempt to set a non-property-list object as an NSUserDefaults

Community
  • 1
  • 1
Glenn
  • 2,808
  • 2
  • 24
  • 30
  • `NSCoding` protocol is used when you archive instance of custom class to `NSData` with `NSKeyedArchiver.archivedDataWithRootObject()` and later retrieve it with `NSKeyedUnarchiver.unarchiveObjectWithData`. – mixel Sep 26 '15 at 16:17
  • @mixel: Please reread again (and remove your down vote): the answer is relevant and the post is actually a duplicate from the referred post. Ok I mentioned NSCodings as well because it was mentioned in the other answer (and not here). But Nicolas is having EXACTLY the same problem and error – Glenn Sep 26 '15 at 16:23