I'm having trouble making a mutable array of objects persistent using NSUserDefaults. I've been trying different code for days and just can't get it right. I've read everything I can find on the subject, including all the postings in StackOverflow. I know I'm close, but I would really appreciate someone pointing out what I'm doing wrong. I know arr in the following code winds up with a count of zero, and that's where I hit a roadblock. I haven't gotten past that point. I'll bet someone will laugh and point our my silly error within 2 minutes of reading my post.
Here's the relevant code, including the log:
Contacts.h:
#interface Contacts : NSObject // <NSCoding>
{
NSMutableArray *arr; // 8/21/13
}
Contacts.m
- (void)encodeWithCoder:(NSCoder *)aCoder
{
NSLog(#"In encodeWithCoder");
NSLog(#"In encodeWithCoder. arr's count is: %i", [arr count]);
{
for (int i = 0; i <[arr count]; i++)
{
NSLog(#"In encodeWithCoder %#", [arr objectAtIndex:i]);
NSLog(#"class %#", [arr class]);
}
[aCoder encodeObject:arr forKey:#"allContacts"];
}
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
NSLog(#"In initWithCoder");
arr = [aDecoder decodeObjectForKey:#"allContacts"];
return self;
}
// The following two methods might not even be needed. I copied them from some examples.
- (NSData *)dataOftype:(NSString *)typeName error:(NSError **)outError
{
return nil;
}
- (BOOL)readFromData:(NSData *)data oftype:(NSString *)typeName error: (NSError **)outError
{
arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"allContacts is ->%#", [arr mutableCopy]);
return YES;
}
ContactStore.h
#interface ContactsStore : NSObject <NSCoding>
{
NSMutableArray *arr; // 8/21/13
NSMutableArray *allContacts;
}
ContactStore.m
- (void)fetchContactsIfNecessary
{
NSLog(#"in ContactsStore.m - fetchContactsIfNecessary #1");
if (!allContacts)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"allContacts"];
allContacts = [[NSMutableArray alloc]initWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:data]];
for (int i = 0; i <[allContacts count]; i++)
{
NSLog(#"In FetchContactsIfNecessary- Gettingfrom NSUserDefaults %#", [allContacts objectAtIndex:i]);
NSLog(#"In FetchContactsIfNecessary- Gettingfrom NSUserDefaults - class %#", [allContacts class]);
}
}
// If we didn't find one in NSUserDefaults, then create a new one.
if (!allContacts)
{
allContacts = [[NSMutableArray alloc] init];
NSLog(#"In FetchContactsIfNecessary-We just allocated allContacts");
}
NSLog(#"in ContactsStore.m - Leaving fetchContactsIfNecessary2");
}
- (BOOL)saveChanges
{
for (int i = 0; i <[allContacts count]; i++)
{
NSLog(#"In saveChanges %#", [allContacts objectAtIndex:i]);
NSLog(#"In saveChanges class %#", [allContacts class]);
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
arr = allContacts; // 8/21/13
NSLog(#"In saveChanges. arr's count is: %i", [arr count]);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:#"allContacts"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"We just synchronized allContacts - I think");
return 1;
}
Log
2013-08-22 14:13:50.764 ImOK[16979:907] In saveChanges Scott Stringer, (212) 888-1234
2013-08-22 14:13:50.772 ImOK[16979:907] In saveChanges class __NSArrayM
2013-08-22 14:13:50.775 ImOK[16979:907] In saveChanges Sheldon Silver, (212) 987-4321
2013-08-22 14:13:50.781 ImOK[16979:907] In saveChanges class __NSArrayM
2013-08-22 14:13:50.793 ImOK[16979:907] In saveChanges. arr's count is: 2
2013-08-22 14:13:50.795 ImOK[16979:907] In encodeWithCoder
2013-08-22 14:13:50.796 ImOK[16979:907] In encodeWithCoder. arr's count is: 0
2013-08-22 14:13:50.798 ImOK[16979:907] In encodeWithCoder
2013-08-22 14:13:50.799 ImOK[16979:907] In encodeWithCoder. arr's count is: 0
2013-08-22 14:13:50.805 ImOK[16979:907] We just synchronized allContacts - I think
The names and phone numbers display correctly after the app returns from the background, but I get just four nulls after relaunching the app. I expected that because arr's count is zero in the method encodeWithCoder, but I can't figure out how to resolve this issue.
Your are only encoding and decoding your object. You don't store it yet. To store it you need a function like this:
- (BOOL)storeBrandOnDevice
{
NSString *path = [self itemArchivePath];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self forKey:#"Contacts"];
[archiver finishEncoding];
BOOL dataSaveSuccess = [data writeToFile:path atomically:YES];
return dataSaveSuccess;
}
to get them from the store something like this:
- (void) getBrandFromStorage
{
NSString *path = [self itemArchivePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:path];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Contacts* storedContacts = [unarchiver decodeObjectForKey:#"Contacts"];
[unarchiver finishDecoding];
}
}
and here is your missing function:
- (NSString*)itemArchivePath
{
#try {
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"items.archive"];
}
#catch (NSException *exception) {
return nil;
}
}
It may not be perfect, but that's the general concept.
For more information about NSCoding, follow Ray Wenderlich's tutorial
Related
This question already has answers here:
NSDictionary with ordered keys
(9 answers)
Closed 6 years ago.
I am passing NSDictionary to my function as a parameter. I want it's key and values to be in order as I inserted.
for eg. expected output is:
mutable dict:{
zKey1 = Value1;
fKey2 = Value2;
aKey3 = Value3;
}
I have tried following ways to create and set value for keys.
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc]init];
[mutableDict setObject:#"Value1" forKey:#"zKey1"];
[mutableDict setObject:#"Value2" forKey:#"fKey2"];
[mutableDict setObject:#"Value3" forKey:#"aKey3"];
NSMutableDictionary *dic2=[[NSMutableDictionary alloc]initWithObjectsAndKeys:#"1004",#"userId",#"cucumber",#"domain",#"168d5c02f ",#"token",#"1004",#"userId1",#"cucumber",#"domain1",#"168d5c02f ",#"token1", nil];
NSDictionary * dict = [NSDictionary
dictionaryWithObjects:#[#"Ravi",#"33",#"India",#"India"]
forKeys:#[#"name",#"age",#"location",#"country"]];
NSArray *sortedKeys = [[dict allKeys] sortedArrayUsingSelector: #selector(compare:)];
NSMutableArray *sortedValues = [NSMutableArray array];
for (NSString *key in sortedKeys) {
[sortedValues addObject: [dict objectForKey: key]];
}
NSString *obj1=#"1004";
NSString *obj2=#"cucumber";
NSString *obj3=#"168d5c02f";
NSString *key1=#" userId";
NSString *key2=#"domain";
NSString *key3=#"token";
NSLog(#"dict %#",dict);
NSDictionary *dict8 =[NSDictionary
dictionaryWithObjects:#[obj1,obj2,obj3]
forKeys:#[key1,key2,key3]];
But nothing has worked I am always getting output as
mutable dict:{
aKey3 = Value3;
fKey2 = Value2;
zKey1 = Value1;
}
dict8 {
domain = cucumber;
token = 168d5c02f;
userId = 1004;
}
dict {
age = 33;
country = India;
location = India;
name = Ravi;
}
dic= {
domain = cucumber;
domain1 = cucumber;
token = "168d5c02f ";
token1 = "168d5c02f ";
userId = 1004;
userId1 = 1004;
}
It is always sorting values according to alphabetical order of keys. Many people say that NSDictionary is an unsorted container. But it does gets sorted. Need help desperately. Thank you in advance.
NSDictionary is not ordered by default. It will always be without any order. To create an ordered dictionary, you will need to override the existing form of the Data structure. You can read This tutorial to achieve your end.
To summarize the tutorial (Because everyone hates link-only answers and links can die any time):
NSDictionary stores its keys in a hash table, which is unordered by design. Since this lack of order is fundamental to the hash table storeage, you have to perform subclassing of NSMutableDictionary (and hence reimplementation of the storage).
In your .h file
//
// OrderedDictionary.h
// OrderedDictionary
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import <Cocoa/Cocoa.h>
#interface OrderedDictionary : NSMutableDictionary
{
NSMutableDictionary *dictionary;
NSMutableArray *array;
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex;
- (id)keyAtIndex:(NSUInteger)anIndex;
- (NSEnumerator *)reverseKeyEnumerator;
#end
In your .m file:
//
// OrderedDictionary.m
// OrderedDictionary
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import "OrderedDictionary.h"
NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent)
{
NSString *objectString;
if ([object isKindOfClass:[NSString class]])
{
objectString = (NSString *)[[object retain] autorelease];
}
else if ([object respondsToSelector:#selector(descriptionWithLocale:indent:)])
{
objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent];
}
else if ([object respondsToSelector:#selector(descriptionWithLocale:)])
{
objectString = [(NSSet *)object descriptionWithLocale:locale];
}
else
{
objectString = [object description];
}
return objectString;
}
#implementation OrderedDictionary
- (id)init
{
return [self initWithCapacity:0];
}
- (id)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity];
array = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
- (void)dealloc
{
//This method is pre-ARC. Manual Release commands don't work now.
//[dictionary release];
//[array release];
//[super dealloc];
}
- (id)copy
{
return [self mutableCopy];
}
- (void)setObject:(id)anObject forKey:(id)aKey
{
if (![dictionary objectForKey:aKey])
{
[array addObject:aKey];
}
[dictionary setObject:anObject forKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[dictionary removeObjectForKey:aKey];
[array removeObject:aKey];
}
- (NSUInteger)count
{
return [dictionary count];
}
- (id)objectForKey:(id)aKey
{
return [dictionary objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator
{
return [array objectEnumerator];
}
- (NSEnumerator *)reverseKeyEnumerator
{
return [array reverseObjectEnumerator];
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex
{
if (![dictionary objectForKey:aKey])
{
[self removeObjectForKey:aKey];
}
[array insertObject:aKey atIndex:anIndex];
[dictionary setObject:anObject forKey:aKey];
}
- (id)keyAtIndex:(NSUInteger)anIndex
{
return [array objectAtIndex:anIndex];
}
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *indentString = [NSMutableString string];
NSUInteger i, count = level;
for (i = 0; i < count; i++)
{
[indentString appendFormat:#" "];
}
NSMutableString *description = [NSMutableString string];
[description appendFormat:#"%#{\n", indentString];
for (NSObject *key in self)
{
[description appendFormat:#"%# %# = %#;\n",
indentString,
DescriptionForObject(key, locale, level),
DescriptionForObject([self objectForKey:key], locale, level)];
}
[description appendFormat:#"%#}\n", indentString];
return description;
}
#end
You can Download Matt Gallagher's orderedDictionary here.
I have an NSMutablearray that I have data saved to. Data is input on my main ViewController. When you tap on a saved item, the SecondViewController has a textfield that the data entry is loaded into in order to be edited. I want to be able to edit that item and then save it back to the array.
I am fairly new to Objective-C / Cocoa Touch so please go easy on me.
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSMutableArray *toDoItems = [NSMutableArray array];
for (NSString *todoitem in toDoItems) {
if ([[ToDoItem] isEqualToString:toDoItems]) {
[toDoItems replaceObjectAtIndex:todoitem];
}
}
I know this isnt completely correct and may be completely off base but Im hoping I am moving in the right direction with this.
EDIT
Here is my ToDoItemSvcArchive.m file
#import "ToDoItemSvcArchive.h"
#implementation ToDoItemSvcArchive
NSString *filePath;
NSMutableArray *toDoItems;
- (id) init {
NSArray *dirPaths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
filePath = [[NSString alloc] initWithString: [docsDir
stringByAppendingPathComponent: #"ToDoItems.archive"]];
[self readArchive];
return self;
}
- (void) readArchive {
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: filePath])
{
toDoItems = [NSKeyedUnarchiver unarchiveObjectWithFile: filePath];
}
else
{
toDoItems = [NSMutableArray array];
}
}
- (void) writeArchive {
[NSKeyedArchiver archiveRootObject:toDoItems toFile:filePath];
}
- (ToDoItem *) createToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.createToDoItem: %#", [todoitem description]);
[toDoItems addObject:todoitem];
[self writeArchive];
return todoitem;
}
- (NSMutableArray *) retrieveAllToDoItems {
return toDoItems;
}
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSMutableArray *toDoItems = [NSMutableArray array];
for (NSString *todoitem in toDoItems) {
if ([[ToDoItem] isEqualToString:toDoItems]) {
[toDoItems replaceObjectAtIndex:todoitem];
}
}
// return todoitem;
}
- (ToDoItem *) deleteToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.deleteToDoItem: %#", [todoitem description]);
[toDoItems removeObject:todoitem];
[self writeArchive];
return todoitem;
}
#end
EDIT
I guess what I dont really understand is how to get the actual index of the object that needs to be replaced.
Edit
I think this is better
- (ToDoItem *) updateToDoItem: (ToDoItem *) todoitem {
NSLog(#"ToDoItemSvc.updateToDoItem: %#", [todoitem description]);
NSUInteger index = [toDoItems indexOfObject:todoitem];
[toDoItems replaceObjectAtIndex:0 withObject:todoitem];
return todoitem;
}
Im still not sure how to specify the correct index.
Over complicating the entire thing
Changed todoitemtext to todoitemobject and made it a todoitem rather than an NSString. Now when I click update, it updates the object and when you go back to the list, the view calls reloaddata and the view is updated with the new object
- (IBAction)updateToDoItem:(id)sender {
NSLog(#"updatingToDoItem: entering");
toDoItemObject.todoitem = toDoItem.text;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
toDoItem.text = toDoItemObject.todoitem;
}
Works like a champ now.
I have two methods to save and load NSManagedObject (updated to original):
-(void) saveToCoreData: (TeamManagedObject *)teamSet{
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelDebug);
if(!teamSet) {
self.teamSet = [NSEntityDescription insertNewObjectForEntityForName:#"TeamManagedObject" inManagedObjectContext:self.managedObjectContext];
} else {
self.teamSet = teamSet;
}
[self.teamSettings enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[self.teamSet setValue:obj forKey:key];
}];
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
}
-(id)load:(TeamManagedObject *)managedObject {
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([TeamManagedObject class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
NSString *value = [managedObject valueForKey:key];
if (value) {
[self setObject:value forKey:key];
}
}
self.teamSet = managedObject;
free(properties);
return self;
}
1) call method [self save:nil] - object creates and saves to CoreData correctly. (value is correct after having app restarted)
2) restarting app and calling load method - it was loaded correctly
3) calling save [self save:object] method with loaded object - it looks like saved, but only before app was restarted... after app restarting, object have an old value...
Where is the mistake?
Thanks!
The following code is in my implementation file:
NSMutableArray *courseArray;
- (IBAction)btnClick:(id)sender
{
NSDictionary *courseNames;
if(![_txtBox.text isEqual:#""]) //if not empty
{
courseNames = [self retrieveCourseNamesForSemester:_txtBox.text];
for (NSString *key in courseNames)
{
NSString *val = [NSString stringWithFormat:#"%#-%#",key,[courseNames objectForKey:key]];
_txtView.text = val;
#try
{
[courseArray addObject:val];
}
#catch(NSException *e)
{
NSLog(#"Exception: %# for value = %#", e, val);
}
}
}
[_coursePicker reloadAllComponents];
_coursePicker.hidden=false;
[_txtBox resignFirstResponder];
}
Where you see the call to NSLog(), I get the following error message:
2014-03-29 00:02:25.830 WebServiceTest[44646:60b] Exception: -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x8d82c30 for value = 73-522-Course Name
EDIT: Also, courseArray is populated with sample data in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
courseArray = #[#"Australia (AUD)", #"China (CNY)",
#"France (EUR)", #"Great Britain (GBP)", #"Japan (JPY)"];
}
Is there somewhere I should be defining that courseArray will take NSString objects?
The code in viewDidLoad creates an immutable array. You need to make a mutable copy, like this
(void)viewDidLoad
{
[super viewDidLoad];
courseArray = [#[#"(AUD)", #"(CNY)", #"(EUR)"] mutableCopy];
}
Try this code,
for (NSString *key in courseNames)
{
NSString *val = [NSString stringWithFormat:#"%#-%#",key,[courseNames objectForKey:key]];
_txtView.text = val;
if ([CourseArray count]==0)
{
CourseArray= [NSMutableArray arrayWithObject:val];
}
else
{
[CourseArray addObject:val];
}
}
i had read this topic How to save My Data Type in NSUserDefault? and get from there this useful part of code:
MyObject *myObject = [[MyObject alloc] init];
NSData *myObjectData = [NSData dataWithBytes:(void *)&myObject length:sizeof(myObject)];
[[NSUserDefaults standardUserDefaults] setObject:myObjectData forKey:#"kMyObjectData"];
for saving data and this for reading
NSData *getData = [[NSData alloc] initWithData:[[NSUserDefaults standardUserDefaults] objectForKey:#"kMyObjectData"]];
MyObject *getObject;
[getData getBytes:&getObject];
its works very good when i save data in one ViewController and read it in other.
but when i whant to use it in the same class:
- (IBAction)linkedInLog:(UIButton *)sender
{
NSUserDefaults *myDefaults = [[NSUserDefaults standardUserDefaults] objectForKey:#"linkedinfo"];
NSData *getData = [[NSData alloc] initWithData:myDefaults];
LinkedContainer *getObject;
[getData getBytes:&getObject];
if (!myDefaults) {
mLogInView = [[linkedInLoginView alloc]initWithNibName:#"linkedInLogInView" bundle:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loginViewDidFinish:)
name:#"loginViewDidFinish"
object:mLogInView];
[self.navigationController pushViewController:mLogInView animated:YES];
if ((FBSession.activeSession.isOpen)&&(mLinkedInIsLogegOn)) {
mMergeButton.hidden = NO;
}
}
else{
mLinkedInIsLogegOn= YES;
mLinkedInInfo.mConsumer = getObject.mConsumer;
mLinkedInInfo.mToken = getObject.mToken;
}
}
something going wrong. in #selector:loginViewDidFinish i am saving my data to NSUserDefaults:
-(void) loginViewDidFinish:(NSNotification*)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
mLinkedInInfo.mConsumer = mLogInView.consumer;
mLinkedInInfo.mToken = mLogInView.accessToken;
NSData *myObjectData = [NSData dataWithBytes:(void *)&mLinkedInInfo length:sizeof(mLinkedInInfo)];
NSUserDefaults *lSave = [NSUserDefaults standardUserDefaults];
[lSave setObject:myObjectData forKey:#"linkedinfo"];
[lSave synchronize];
if (mLinkedInInfo.mToken) {
mLinkedInIsLogegOn = YES;
}
}
the program always crashes when it comes to else part. If somebody knows what I am doing wrong please help me)
error message: Thread 1 : EXC_BAD_ACCESS(code=2,address 0x8) when compiling getObject.Consumer
In the vast majority of cases, this is not going to be a meaningful way to serialize your object into an NSData:
MyObject *myObject = [[MyObject alloc] init];
NSData *myObjectData = [NSData dataWithBytes:(void *)&myObject length:sizeof(myObject)];
[[NSUserDefaults standardUserDefaults] setObject:myObjectData forKey:#"kMyObjectData"];
The canonical way to do this would be for MyObject to adopt the NSCoding protocol. Based on the code you posted here, an adoption of NSCoding might look like this:
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super init])
{
mConsumer = [coder decodeObjectForKey: #"consumer"];
mToken = [coder decodeObjectForKey: #"token"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:mConsumer forKey: #"consumer"];
[coder encodeObject:mToken forKey:#"token"];
}
Once you had done that work, you would convert MyObject to and from NSData like this:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject: myObject];
MyObject* myObject = (MyObject*)[NSKeyedUnarchiver unarchiveObjectWithData: data];
The code you have here is totally going to smash the stack and crash (because this line [getData getBytes:&getObject]; will cause the NSData to write bytes to the address of getObject, which is locally declared on the stack. Hence stack smashing.) Starting from your code, a working implementation might look something like this:
- (IBAction)linkedInLog:(UIButton *)sender
{
NSData* dataFromDefaults = [[NSUserDefaults standardUserDefaults] objectForKey:#"linkedinfo"];
LinkedContainer* getObject = (LinkedContainer*)[NSKeyedUnarchiver unarchiveObjectWithData: dataFromDefaults];
if (!dataFromDefaults) {
mLogInView = [[linkedInLoginView alloc]initWithNibName:#"linkedInLogInView" bundle:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loginViewDidFinish:)
name:#"loginViewDidFinish"
object:mLogInView];
[self.navigationController pushViewController:mLogInView animated:YES];
if ((FBSession.activeSession.isOpen)&&(mLinkedInIsLogegOn)) {
mMergeButton.hidden = NO;
}
}
else{
mLinkedInIsLogegOn= YES;
mLinkedInInfo.mConsumer = getObject.mConsumer;
mLinkedInInfo.mToken = getObject.mToken;
}
}
-(void) loginViewDidFinish:(NSNotification*)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
mLinkedInInfo.mConsumer = mLogInView.consumer;
mLinkedInInfo.mToken = mLogInView.accessToken;
NSData* objectData = [NSKeyedArchiver archivedDataWithRootObject: mLinkedInInfo];
[[NSUserDefaults standardUserDefaults] setObject: objectData forKey: #"linkedinfo"];
[[NSUserDefaults standardUserDefaults] synchronize];
if (mLinkedInInfo.mToken) {
mLinkedInIsLogegOn = YES;
}
}
I agree with ipmcc 's answer, another viable option would be to add methods to your object to convert it to an NSDictionary. You could add methods to -initWithDictionary as well and should make instantiation very easy. Pull from dictionary in NSUserDefaults to use, convert to dictionary to save.
Here is an example of those 2 methods with generic data:
- (id)initWithDictionary:(NSDictionary *)dict
{
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if(self && [dict isKindOfClass:[NSDictionary class]]) {
NSObject *receivedFences = [dict objectForKey:#"fences"];
NSMutableArray *parsedFences = [NSMutableArray array];
if ([receivedFences isKindOfClass:[NSArray class]]) {
for (NSDictionary *item in (NSArray *)receivedFences) {
if ([item isKindOfClass:[NSDictionary class]]) {
[parsedFences addObject:[Fences modelObjectWithDictionary:item]];
}
}
}
}
// More checks for specific objects here
return self;
}
- (NSDictionary *)dictionaryRepresentation
{
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
NSMutableArray *tempArrayForFences = [NSMutableArray array];
for (NSObject *subArrayObject in self.fences) {
if([subArrayObject respondsToSelector:#selector(dictionaryRepresentation)]) {
// This class is a model object
[tempArrayForFences addObject:[subArrayObject performSelector:#selector(dictionaryRepresentation)]];
} else {
// Generic object
[tempArrayForFences addObject:subArrayObject];
}
}
[mutableDict setValue:[NSArray arrayWithArray:tempArrayForFences] forKey:#"fences"];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
This is basically boilerplate code that is generated by a program I use called JSON Accelerator. It will read a JSON string returned by an API and generate object code for you. Not really a new concept, but makes created classes for API's very easy. And this bit of code works great for creating dictionaries to be saved to NSUserDefaults. Hope this helps.