NSKeyedArchiver and NSKeyedUnarchiver

You now have a place to save data on the filesystem and a model object that can be saved to the filesystem. The final two questions are: how do you kick off the saving and loading processes and when do you do it? To save instances of BNRItem, you will use the class NSKeyedArchiver when the application exits.

In BNRItemStore.h, declare a new method.

-​ ​(​B​O​O​L​)​s​a​v​e​C​h​a​n​g​e​s​;​

Implement this method in BNRItemStore.m to send the message archiveRootObject:toFile: to the NSKeyedArchiver class.

-​ ​(​B​O​O​L​)​s​a​v​e​C​h​a​n​g​e​s​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​s​e​l​f​ ​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​]​;​

 ​ ​ ​ ​/​/​ ​R​e​t​u​r​n​s​ ​Y​E​S​ ​o​n​ ​s​u​c​c​e​s​s​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​K​e​y​e​d​A​r​c​h​i​v​e​r​ ​a​r​c​h​i​v​e​R​o​o​t​O​b​j​e​c​t​:​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​o​F​i​l​e​:​p​a​t​h​]​;​
}​

The archiveRootObject:toFile: method takes care of saving every single BNRItem in privateItems to the itemArchivePath. Yes, it is that simple. Here is how archiverRootObject:toFile: works:

  • The method begins by creating an instance of NSKeyedArchiver. (NSKeyedArchiver is a concrete subclass of the abstract class NSCoder.)

  • privateItems is sent the message encodeWithCoder: and is passed the instance of NSKeyedArchiver as an argument.

  • The privateItems array then sends encodeWithCoder: to all of the objects it contains, passing the same NSKeyedArchiver. Thus, all your instances of BNRItem encode their instance variables into the very same NSKeyedArchiver (Figure 18.4).

  • The NSKeyedArchiver writes the data it collected to the path.

Figure 18.4  Archiving the privateItems array

Archiving the privateItems array

When the user presses the Home button on the device, the message applicationDidEnterBackground: is sent to the BNRAppDelegate. That is when you want to send saveChanges to the BNRItemStore.

In BNRAppDelegate.m, implement applicationDidEnterBackground: to kick off saving the items. Make sure to import the header file for BNRItemStore at the top of this file.

#​i​m​p​o​r​t​ ​"​B​N​R​I​t​e​m​S​t​o​r​e​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​A​p​p​D​e​l​e​g​a​t​e​

-​ ​(​v​o​i​d​)​a​p​p​l​i​c​a​t​i​o​n​D​i​d​E​n​t​e​r​B​a​c​k​g​r​o​u​n​d​:​(​U​I​A​p​p​l​i​c​a​t​i​o​n​ ​*​)​a​p​p​l​i​c​a​t​i​o​n​
{​
 ​ ​ ​ ​B​O​O​L​ ​s​u​c​c​e​s​s​ ​=​ ​[​[​B​N​R​I​t​e​m​S​t​o​r​e​ ​s​h​a​r​e​d​S​t​o​r​e​]​ ​s​a​v​e​C​h​a​n​g​e​s​]​;​
 ​ ​ ​ ​i​f​ ​(​s​u​c​c​e​s​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​S​a​v​e​d​ ​a​l​l​ ​o​f​ ​t​h​e​ ​B​N​R​I​t​e​m​s​"​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​C​o​u​l​d​ ​n​o​t​ ​s​a​v​e​ ​a​n​y​ ​o​f​ ​t​h​e​ ​B​N​R​I​t​e​m​s​"​)​;​
 ​ ​ ​ ​}​
}​

(This method may have already been implemented by the template. If so, make sure to add code to the existing method instead of writing a brand new one.)

Build and run the application on the simulator. Create a few instances of BNRItem. Then, press the Home button to leave the application. Check the console, and you should see a log statement indicating that the items were saved.

While you cannot yet load these instances of BNRItem back into the application, you can still verify that something was saved. In Finder, press Command-Shift-G. Then, type in ~/Library/Application Support/iPhone Simulator and press Enter. This is where all of the applications and their sandboxes are stored for the simulator.

Open the directory 7.0 (or, if you are working with another version of iOS, select that directory). Open Applications to see the list of every application that has run on your simulator using iOS 7.0. Unfortunately, these applications have really unhelpful names. You have to dig into each directory to find the one that contains Homepwner.

Figure 18.5  Homepwner’s sandbox

Homepwner’s sandbox

In Homepwner’s directory, navigate into the Documents directory (Figure 18.5). You will see the items.archive file. Here is a tip: make an alias to the iPhone Simulator directory somewhere convenient to make it easy to check the sandboxes of your applications.

Now let’s turn to loading these files. To load instances of BNRItem when the application launches, you will use the class NSKeyedUnarchiver when the BNRItemStore is created.

In BNRItemStore.m, add the following code to initPrivate.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​P​r​i​v​a​t​e​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​_​p​r​i​v​a​t​e​I​t​e​m​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​s​e​l​f​ ​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​p​r​i​v​a​t​e​I​t​e​m​s​ ​=​ ​[​N​S​K​e​y​e​d​U​n​a​r​c​h​i​v​e​r​ ​u​n​a​r​c​h​i​v​e​O​b​j​e​c​t​W​i​t​h​F​i​l​e​:​p​a​t​h​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​f​ ​t​h​e​ ​a​r​r​a​y​ ​h​a​d​n​'​t​ ​b​e​e​n​ ​s​a​v​e​d​ ​p​r​e​v​i​o​u​s​l​y​,​ ​c​r​e​a​t​e​ ​a​ ​n​e​w​ ​e​m​p​t​y​ ​o​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​_​p​r​i​v​a​t​e​I​t​e​m​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​p​r​i​v​a​t​e​I​t​e​m​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

The unarchiveObjectWithFile: method will create an instance of NSKeyedUnarchiver and load the archive located at the itemArchivePath into that instance. The NSKeyedUnarchiver will then inspect the type of the root object in the archive and create an instance of that type. In this case, the type will be an NSMutableArray because you created this archive with a root object of this type. (If the root object was a BNRItem instead, unarchiveObjectWithFile: would return an instance of BNRItem.)

The newly allocated NSMutableArray is then sent initWithCoder: and, as you may have guessed, the NSKeyedUnarchiver is passed as the argument. The array starts decoding its contents (instances of BNRItem) from the NSKeyedUnarchiver and sends each of these objects the message initWithCoder:, passing the same NSKeyedUnarchiver.

You can now build and run the application. Any items that a user enters will be available until the user explicitly deletes them. One thing to note about testing your saving and loading code: If you kill Homepwner from Xcode, applicationDidEnterBackground: will not get a chance to be called and the item array will not be saved. You must press the Home button first and then kill it from Xcode by clicking the Stop button.

Now that you can save and load items, there is no reason to auto-populate each one with random data. In BNRItemStore.m, modify the implementation of createItem so that it creates an empty BNRItem instead of one with random data.

-​ ​(​B​N​R​I​t​e​m​ ​*​)​c​r​e​a​t​e​I​t​e​m​
{​
 ​ ​ ​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​ ​=​ ​[​B​N​R​I​t​e​m​ ​r​a​n​d​o​m​I​t​e​m​]​;​
 ​ ​ ​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​ ​=​ ​[​[​B​N​R​I​t​e​m​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​a​d​d​O​b​j​e​c​t​:​i​t​e​m​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​i​t​e​m​;​
}​
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset