Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 334 Vote(s) - 3.53 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Attempt to set a non-property-list object as an NSUserDefaults

#1
I thought I knew what was causing this error, but I can't seem to figure out what I did wrong.

Here is the full error message I am getting:

<pre>
Attempt to set a non-property-list object (
"<BC_Person: 0x8f3c140>"
) as an NSUserDefaults value for key personDataArray
</pre>

I have a `Person` class that I think is conforming to the `NSCoding` protocol, where I have both of these methods in my person class:

- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.personsName forKey:@"BCPersonsName"];
[coder encodeObject:self.personsBills forKey:@"BCPersonsBillsArray"];
}

- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.personsName = [coder decodeObjectForKey:@"BCPersonsName"];
self.personsBills = [coder decodeObjectForKey:@"BCPersonsBillsArray"];
}
return self;
}

At some point in the app, the `NSString` in the `BC_PersonClass` is set, and I have a `DataSave` class that I think is handling the encoding the properties in my `BC_PersonClass`.
Here is the code I am using from the `DataSave` class:

- (void)savePersonArrayData:(BC_Person *)personObject
{
// NSLog(@"name of the person %@", personObject.personsName);

[mutableDataArray addObject:personObject];

// set the temp array to the mutableData array
tempMuteArray = [NSMutableArray arrayWithArray:mutableDataArray];

// save the person object as nsData
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];

// first add the person object to the mutable array
[tempMuteArray addObject:personEncodedObject];

// NSLog(@"Objects in the array %lu", (unsigned long)mutableDataArray.count);

// now we set that data array to the mutable array for saving
dataArray = [[NSArray alloc] initWithArray:mutableDataArray];
//dataArray = [NSArray arrayWithArray:mutableDataArray];

// save the object to NS User Defaults
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:dataArray forKey:@"personDataArray"];
[userData synchronize];
}

I hope this is enough code to give you an idea o what I am trying to do.
Again I know my problem lie with how I am encoding my properties in my BC_Person class, I just can't seem to figure out what though I'm doing wrong.

Thanks for the help!




Reply

#2
First off, rmaddy's answer (above) is right: implementing `NSCoding` doesn't help. However, you need to implement `NSCoding` to use `NSKeyedArchiver` and all that, so it's just one more step... converting via `NSData`.

Example methods

- (NSUserDefaults *) defaults {
return [NSUserDefaults standardUserDefaults];
}

- (void) persistObj:(id)value forKey:(NSString *)key {
[self.defaults setObject:value forKey:key];
[self.defaults synchronize];
}

- (void) persistObjAsData:(id)encodableObject forKey:(NSString *)key {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encodableObject];
[self persistObj:data forKey:key];
}

- (id) objectFromDataWithKey:(NSString*)key {
NSData *data = [self.defaults objectForKey:key];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}

So you can wrap your `NSCoding` objects in an `NSArray` or `NSDictionary` or whatever...
Reply

#3
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.

When you read back the array you need to unarchive the `NSData` to get back your `BC_Person` objects.

Perhaps you want this:

- (void)savePersonArrayData:(BC_Person *)personObject {
[mutableDataArray addObject:personObject];

NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
for (BC_Person *personObject in mutableDataArray) {
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
[archiveArray addObject:personEncodedObject];
}

NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:archiveArray forKey:@"personDataArray"];
}
Reply

#4
I had this problem trying save a dictionary to `NSUserDefaults`. It turns out it wouldn't save because it contained `NSNull` values. So I just copied the dictionary into a mutable dictionary removed the nulls then saved to `NSUserDefaults`

NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary_trying_to_save];
[dictionary removeObjectForKey:@"NullKey"];
[[NSUserDefaults standardUserDefaults] setObject:dictionary forKey:@"key"];

In this case I knew which keys might be `NSNull` values.
Reply

#5
It seems rather wasteful to me to run through the array and encode the objects into NSData yourself. Your error `BC_Person is a non-property-list object` is telling you that the framework doesn't know how to serialize your person object.

So all that is needed is to ensure that your person object conforms to NSCoding then you can simply convert your array of custom objects into NSData and store that to defaults. Heres a playground:

**Edit**: Writing to `NSUserDefaults` is broken on Xcode 7 so the playground will archive to data and back and print an output. The UserDefaults step is included in case its fixed at a later point

//: Playground - noun: a place where people can play

import Foundation

class Person: NSObject, NSCoding {
let surname: String
let firstname: String

required init(firstname:String, surname:String) {
self.firstname = firstname
self.surname = surname
super.init()
}

//MARK: - NSCoding -
required init(coder aDecoder: NSCoder) {
surname = aDecoder.decodeObjectForKey("surname") as! String
firstname = aDecoder.decodeObjectForKey("firstname") as! String
}

func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(firstname, forKey: "firstname")
aCoder.encodeObject(surname, forKey: "surname")
}
}

//: ### Now lets define a function to convert our array to NSData

func archivePeople(people:[Person]) -> NSData {
let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
return archivedObject
}

//: ### Create some people

let people = [Person(firstname: "johnny", surname:"appleseed"),Person(firstname: "peter", surname: "mill")]

//: ### Archive our people to NSData

let peopleData = archivePeople(people)

if let unarchivedPeople = NSKeyedUnarchiver.unarchiveObjectWithData(peopleData) as? [Person] {
for person in unarchivedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Failed to unarchive people")
}

//: ### Lets try use NSUserDefaults
let UserDefaultsPeopleKey = "peoplekey"
func savePeople(people:[Person]) {
let archivedObject = archivePeople(people)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(archivedObject, forKey: UserDefaultsPeopleKey)
defaults.synchronize()
}

func retrievePeople() -> [Person]? {
if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsPeopleKey) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person]
}
return nil
}

if let retrievedPeople = retrievePeople() {
for person in retrievedPeople {
print("\(person.firstname), you have been unarchived")
}
} else {
print("Writing to UserDefaults is still broken in playgrounds")
}

And Voila, you have stored an array of custom objects into NSUserDefaults
Reply

#6
To save:

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
[currentDefaults setObject:data forKey:@"yourKeyName"];

To Get:

NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];


For Remove

[currentDefaults removeObjectForKey:@"yourKeyName"];
Reply

#7
#Swift 3 Solution#

## Simple utility class##

class ArchiveUtil {

private static let PeopleKey = "PeopleKey"

private static func archivePeople(people : [Human]) -> NSData {

return NSKeyedArchiver.archivedData(withRootObject: people as NSArray) as NSData
}

static func loadPeople() -> [Human]? {

if let unarchivedObject = UserDefaults.standard.object(forKey: PeopleKey) as? Data {

return NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? [Human]
}

return nil
}

static func savePeople(people : [Human]?) {

let archivedObject = archivePeople(people: people!)
UserDefaults.standard.set(archivedObject, forKey: PeopleKey)
UserDefaults.standard.synchronize()
}

}

##Model Class

class Human: NSObject, NSCoding {

var name:String?
var age:Int?

required init(n:String, a:Int) {

name = n
age = a
}


required init(coder aDecoder: NSCoder) {

name = aDecoder.decodeObject(forKey: "name") as? String
age = aDecoder.decodeInteger(forKey: "age")
}


public func encode(with aCoder: NSCoder) {

aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")

}
}


##How to call##

var people = [Human]()

people.append(Human(n: "Sazzad", a: 21))
people.append(Human(n: "Hissain", a: 22))
people.append(Human(n: "Khan", a: 23))

ArchiveUtil.savePeople(people: people)

let others = ArchiveUtil.loadPeople()

for human in others! {

print("name = \(human.name!), age = \(human.age!)")
}
Reply

#8
>

[To see links please register here]

>
> 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. For more details, see Preferences and Settings Programming Guide.
Reply

#9
Swift- 4 Xcode 9.1
--------

**try this code**

**you can not store mapper in NSUserDefault, you can only store NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.**

let myData = NSKeyedArchiver.archivedData(withRootObject: myJson)
UserDefaults.standard.set(myData, forKey: "userJson")

let recovedUserJsonData = UserDefaults.standard.object(forKey: "userJson")
let recovedUserJson = NSKeyedUnarchiver.unarchiveObject(with: recovedUserJsonData)
Reply

#10
***Swift 5*** Very Easy way

//MARK:- First you need to encoded your arr or what ever object you want to save in UserDefaults
//in my case i want to save Picture (NMutableArray) in the User Defaults in
//in this array some objects are UIImage & Strings

//first i have to encoded the NMutableArray
let encodedData = NSKeyedArchiver.archivedData(withRootObject: yourArrayName)
//MARK:- Array save in UserDefaults
defaults.set(encodedData, forKey: "YourKeyName")

//MARK:- When you want to retreive data from UserDefaults
let decoded = defaults.object(forKey: "YourKeyName") as! Data
yourArrayName = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! NSMutableArray

//MARK: Enjoy this arrry "yourArrayName"
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through