55

I have a custom object, a UIImageView subclass which has a few gestureRecognizer objects.

If I have a number of these objects stored in a NSMutableArray, how would this array of objects be saved to disk so that it can be loaded when the user runs the app again?

I would like to load the array from the disk and use the objects.

4 Answers 4

117

My implementation for something similar is the following and works perfectly :

The custom object (Settings) should implement the protocol NSCoding :

-(void)encodeWithCoder:(NSCoder *)encoder{
    [encoder encodeObject:self.difficulty forKey:@"difficulty"];
    [encoder encodeObject:self.language forKey:@"language"];
    [encoder encodeObject:self.category forKey:@"category"];
    [encoder encodeObject:self.playerType forKey:@"playerType"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.difficulty = [decoder decodeObjectForKey:@"difficulty"];
        self.language = [decoder decodeObjectForKey:@"language"];
        self.category = [decoder decodeObjectForKey:@"category"];
        self.playerType = [decoder decodeObjectForKey:@"playerType"];
    }
    return self;
}

The following code writes the custom object to a file (set.txt) and then restores it to the array myArray :

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:@"set.txt"];

NSMutableArray *myObject=[NSMutableArray array];
[myObject addObject:self.settings];    

[NSKeyedArchiver archiveRootObject:myObject toFile:appFile]; 

NSMutableArray* myArray = [NSKeyedUnarchiver unarchiveObjectWithFile:appFile]; 
Sign up to request clarification or add additional context in comments.

6 Comments

This should have more votes. Complete solution and really helped me out. Thanks!
FYI: For as long as your custom object complies to NSCoding and implements initWithCoder: and encodeWithCoder:, you can use it as the root object upon archiving. No need to put it in an NSMutableArray as in this code.
FYI: Take a look at this nshipster.com/nscoding if you want more information on how's, when's and why's of NSKeyedArchiver and NSCoding.
Make sure if you are encoding and decoding items like NSUIntegers to handle the NSCoding like this: self.versionNum = [[decoder decodeObjectForKey:@"versionNum"] unsignedIntegerValue]; and [encoder encodeObject:[NSNumber numberWithUnsignedInteger:self.versionNum] forKey:@"versionNum"];
Should if (self = [super init]) be if (self = [super initWithCoder:decoder])?
|
35
//store the array
[NSKeyedArchiver archiveRootObject:myArray toFile:@"someFile"];

//load the array
NSMutableArray* myArray = [NSKeyedUnarchiver unarchiveObjectWithFile:@"someFile"];

Official References.

Note that when the array contains custom object types, you must ensure your type conforms to the NSCoding Protocol before this will work.

3 Comments

Thanks. So this should return the array as it was when saved, no matter what is in the array? Will the array items be retrievable and usable?
I may just be doing it wrong but I don't think code this simple works for custom objects, both methods are returning 'No'.
@Deco - Custom object types must confirm to the NSCoding Protocol for this to work.
6

This blog post explains how to store an array of custom objects to disk using NSKeyedArchiver and read it back with NSKeyedUnarchiver:

http://www.cocoabuilder.com/archive/cocoa/240775-saving-nsarray-of-custom-objects.html

Apple also has a very helpful guide on the matter, the Archives and Serializations Programming Guide.

Comments

2

I would like to share my improvements to Kostas solution, if somebody needs them.

  1. A class name can be used to generate text file name to store object.
  2. It is a good solution to save objects of a view controller in viewWillDisappear method and restore them in viewDidLoad method.
  3. File name should be generated in separate method to avoid duplicate code.
  4. After restoring object, it should be checked to be not nil.

- (void)viewDidLoad
{
  [super viewDidLoad];

  // Restoring form object from the file
  NSString *formFilePath = [self formFilePath];
  RRCreateResumeForm *form = [NSKeyedUnarchiver unarchiveObjectWithFile:formFilePath];
  if (form != nil) {
    self.formController.form = form;
  }
}


- (void)viewWillDisappear:(BOOL)animated
{
  [super viewWillDisappear:animated];

  // Saving the form object to the file
  NSString *formFilePath = [self formFilePath];
  [NSKeyedArchiver archiveRootObject:self.formController.form toFile:formFilePath];
}


// Returns a file path to the file with stored form data for form controller
- (NSString *)formFilePath
{
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];
  NSString *formClassName = NSStringFromClass( [self.formController.form class] );
  NSString *formFileName = [NSString stringWithFormat:@"%@.txt", formClassName];
  NSString *formFilePath = [documentsDirectory stringByAppendingPathComponent:formFileName];

  return formFilePath;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.