Problems with NSKeyedArchiver- Simply does not archive at all! - iphone

I am probably not seeing something here, that is why I am asking for help :)
Here is the deal I have a NSMutable array of items that fulfill the NSCoding protocol, but NSKeyedArchiver always fails to archive it... here is my object implementation:
#implementation YTVideo
#synthesize URL,thumb,titulo;
#pragma mark NSCoding
#define kTituloKey #"titulo"
#define kURLKey #"URL"
#define kThumbKey #"thumb"
-(id)initWithData:(NSString *)ktitle :(UIImage *)kThumb :(NSURL *)kURL{
self.titulo = ktitle;
self.thumb = kThumb;
self.URL = kURL;
return self;
}
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:titulo forKey:kTituloKey];
[encoder encodeObject:URL forKey:kURLKey];
NSData *thumbData = UIImagePNGRepresentation(thumb);
[encoder encodeObject:thumbData forKey:kThumbKey];
}
- (id)initWithCoder:(NSCoder *)decoder {
NSString* ktitulo = [decoder decodeObjectForKey:kTituloKey];
NSURL* kURL = [decoder decodeObjectForKey:kURLKey];
NSData* kThumbdata = [decoder decodeObjectForKey:kThumbKey];
UIImage* kThumb=[UIImage imageWithData:kThumbdata];
return [self initWithData:ktitulo:kThumb:kURL];
}
#end
During the program execution I have a NSMutable array of those objects called videosArray.
then, eventually, I try:
NSString* path =[NSHomeDirectory() stringByAppendingPathComponent:#"teste.wrapit"];
NSLog(#"PATH =%#",path);
bool teste = [NSKeyedArchiver archiveRootObject:videosArray toFile:path];
NSLog(#"aramzenamento:%#",teste ? #"sucesso!" :#"Nope");
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
NSLog(#"Arquivo armazenado existe?%#",fileExists ?#"Sim":#"Nao");
And I always get a fail on my boolean checks...
Any Ideas where I am completely wrong??
Thanks!!

The problem you're experiencing has nothing to do with NSKeyedArchiver. By the looks of it, you're trying to archive your object at the root-level of your sandbox (the directory returned by NSHomeDirectory()). Try replacing the first line of the second block of code with
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
path = [path stringByAppendingPathComponent:#"teste.wrapit"];
Another (perhaps cleaner) way to get the path of your Documents folder is to use the C function NSSearchPathForDirectoriesInDomains, which returns an array of paths that match the first argument:
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"teste.wrapit"];
It's worth pointing out that, on iOS, the function NSSearchPathForDirectoriesInDomain is guaranteed to return an NSArray with a single element when you use a built-in constant (like NSDocumentDirectory) for the first argument, so you can safely use objectAtIndex:0 on the array.

Related

Saving in NSDocumentDirectory or NSCachesDirectory

Have tried storing my NSMutableArray's object to NSUserDefaults but, no luck.
My NSMutableArray contains this log right here:
`ALAsset - Type:Photo, URLs:assets-library://asset/asset.JPG?id=92A7A24F-D54B-496E-B250-542BBE37BE8C&ext=JPG`
I know that its a ALAsset object, in the AGImagePickerController it is compared as NSDictionary, so what I needed to do is save the NSDictionary or the Array I used to where I store my ALAsset object then save it in either in NSDocu or NSCaches as a file then retrieve it again (This was my idea).
But the problem is,Though I tried this code but not working, and doesn't display anything in NSDocu or NSCache Directories.
First try (info is the one that contains ALAsset object):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *filePath = [basePath stringByAppendingPathComponent:#"filename.plist"];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:filePath];
NSString *error;
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
if(plistData) {
[info writeToFile:filePath atomically:YES];
} else {
NSLog(error);
}
Second try:
- (NSString *)createEditableCopyOfFileIfNeeded:(NSString *)_filename {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableFilePath = [documentsDirectory stringByAppendingPathComponent: _filename ];
success = [fileManager fileExistsAtPath:writableFilePath];
if (success) return writableFilePath;
// The writable file does not exist, so copy the default to the appropriate location.
NSString *defaultFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: _filename ];
success = [fileManager copyItemAtPath:defaultFilePath toPath:writableFilePath error:&error];
if (!success) {
NSLog([error localizedDescription]);
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
return writableFilePath;
}
Save it this way:
NSString *writableFilePath = [self createEditableCopyOfFileIfNeeded:[NSString stringWithString:#"hiscores"]];
if (![info writeToFile:writableFilePath atomically:YES]){
NSLog(#"WRITE ERROR");
}
Third try:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:??????];
[info writeToFile:filePath atomically:YES];
Fourth try(Unsure of because of its modifying in the appbundle):
https://stackoverflow.com/a/6311129/1302274
Is there other way? Hope someone would guide me.
You can store your NSMutableArray to NSUserDefault by archiving it to NSData and than retrieving it by Unarchiving it back to NSMutableArray.
-(NSData*) getArchievedDataFromArray:(NSMutableArray*)arr
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
return data;
}
-(NSMutableArray*) getArrayFromArchievedData:(NSData*)data
{
NSMutableArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return arr;
}
For saving array to NSUserDefault :
[[NSUserDefaults standardUserDefaults] setObject:[self getArchievedDataFromArray: yourArray] forKey:#"YourKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
For retrieving array back from NSUserDefault :
NSMutableArray *yourArray = [self getArrayFromArchievedData:[[NSUserDefaults standardUserDefaults]objectForKey:#"YourKey"]];
Also you can save Array in form of NSData to a file in NSDocumentDirectory or NSCachesDirectory. Hope this helps....
Edited: An UIImage+NSCoding category
.h file
#import <UIKit/UIKit.h>
#interface UIImage (NSCoding)
- (id) initWithCoderForArchiver:(NSCoder *)decoder;
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder ;
#end
.m file
#import "UIImage+NSCoding.h"
#import <objc/runtime.h>
#define kEncodingKey #"UIImage"
#implementation UIImage (NSCoding)
+ (void) load
{
#autoreleasepool {
if (![UIImage conformsToProtocol:#protocol(NSCoding)]) {
Class class = [UIImage class];
if (!class_addMethod(
class,
#selector(initWithCoder:),
class_getMethodImplementation(class, #selector(initWithCoderForArchiver:)),
protocol_getMethodDescription(#protocol(NSCoding), #selector(initWithCoder:), YES, YES).types
)) {
NSLog(#"Critical Error - [UIImage initWithCoder:] not defined.");
}
if (!class_addMethod(
class,
#selector(encodeWithCoder:),
class_getMethodImplementation(class, #selector(encodeWithCoderForArchiver:)),
protocol_getMethodDescription(#protocol(NSCoding), #selector(encodeWithCoder:), YES, YES).types
)) {
NSLog(#"Critical Error - [UIImage encodeWithCoder:] not defined.");
}
}
}
}
- (id) initWithCoderForArchiver:(NSCoder *)decoder {
if (self = [super init]) {
NSData *data = [decoder decodeObjectForKey:kEncodingKey];
self = [self initWithData:data];
}
return self;
}
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {
NSData *data = UIImagePNGRepresentation(self);
[encoder encodeObject:data forKey:kEncodingKey];
}
#end
The documentation of NSArray for the "writeToFile:atomically:" method, shows that all members must be property list objects. ALAsset is not a property list object, so writing that to a file is not going to work.
I know that its a ALAsset object, in the AGImagePickerController it is
compared as NSDictionary
If you looked carefully then you would have seen that it does not compare ALAsset's, but their 'ALAssetPropertyURLs' property. The value of that property is an NSDictionary.
As ALAsset does not have a public constructor, there is no way you can reconstruct it after reading from a file or NSUserDefaults, even if you manage to write it.
So the best thing you can do is to re-fetch the ALAssets from the source that you originally got them from. I assume that is an ALAssetsGroup? Instead of saving to file and retrieving again, why don't you just regenerate them with the same query on ALAssetsGroup as you originally used to generate them?
EDIT:
So you say you got the original ALAsset's from an AGImagePickerController. In order to store them, you can take Matej's advice in the comments and store the URLs that identify them.
But keep in mind that AGImagePickerController is a means for the user to pick a number of photos and then do something with them. That is, the ALAssets are simply intermediare results pointing to the original locations of the photos. If you store the URL's and retrieve them later, there is no guarantee at all that the originals are still there.
So ask yourself: what is it that you want the user to do with the photos, and store the result of that action, rather than the assets themselves. For example, one reasonable action you could do is to create a new ALAssetGroup (with the addAssetsGroupAlbumWithName: method on ALAssetsLibrary), and store the assets in there. ALAssetGroups are automatically saved, so you don't need to do anything yourself for that.
EDIT 2 - after more information from the OP
What Matej hints at in the comments, is to convert the array of ALAssets that you have into an array of dictionaries by retrieving the urls from the assets. As you can read in the ALAsset class documentation you can do that in the following way:
NSArray *assetArray = // your array of ALAssets
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:assetArray.count];
for( ALAsset *asset in assetArray ) {
NSDictionary *urlDictionary = [asset valueForProperty:#"ALAssetPropertyURLs"];
[urls addObject:urlDictionary];
}
The resulting array of dictionaries you can save in any way you like.
After restart of your app, you read the array of dictionaries back from where you stored it. Then Matej suggests to use ALAssetsLibrary's assetForURL:resultBlock:failureBlock: to recreate the ALAssets. But as we now know you want to put a checkmark on the original assets again, it is better to fetch the original array of ALAssets, and check whether any of them are present in the recovered urls. The following should work for that:
NSArray *assetArray = // the full array of ALAssets from AGImagePickerController
NSArray *urls = // the recovered array of NSDictionaries
for( ALAsset *asset in assetArray ) {
NSDictionary *urlDictionary = [asset valueForProperty:#"ALAssetPropertyURLs"];
if( [urls containsObject:urlDictionary] ) {
... // set checkmark on asset
}
}
This assumes the original assets have not changed, which is not under your control (the user has removed/added photos, for example).
This is the method I use for storing array or dictionary objects.
- (NSArray*)readPlist
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"filename.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if (![fMgr fileExistsAtPath:plistPath]) {
[self writePlist:[NSArray array]];
}
return [NSArray arrayWithContentsOfFile:plistPath];
}
- (void)writePlist:(NSArray*)arr
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"filename.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if ([fMgr fileExistsAtPath:plistPath])
[fMgr removeItemAtPath:plistPath error:nil];
[arr writeToFile:plistPath atomically:YES];
}

plist values being lost

Okay I have spent the last 2 days trying to sort this one out. I have a class thats controlling my property list, It is used as a singleton class so its only instantiated once and it has one other method in it that is used to save the values to the plist.
The plist works sweet no matter what you do up untill you remove the app from the multitasking bar (i.e. close the app completely) then all of the values in the plist are reset to null.. However the plist is not lost.
So I am thinking now maybe I am overwriting the values in my plist that I have made in the root document directory for read and write capability. I am hoping with my code example you guys might be able to spot my mistake... I'm not sure what else I need to add.. if you have any question that will help you help me answer this problem then please let me know.
any help would be hugely appreciated and my children's children's children will be in debuted to you.
#import "EnginePropertiesController.h"
static EnginePropertiesController *sharedMyManager = nil;
#implementation EnginePropertiesController
#synthesize pSignature;
#synthesize pVersion;
#synthesize rNumber;
#synthesize dVReturned;
#synthesize cacheValue;
#synthesize manu;
#synthesize mod;
#synthesize submod;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
NSLog(#"myplist path read = %#", plistPath);
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *tempRoot = (NSMutableDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
manu = [cacheValue objectForKey:#"Manu"];
mod = [cacheValue objectForKey:#"Mod"];
submod = [cacheValue objectForKey:#"SubMod"];
if (tempRoot && [tempRoot count]){
// assign values
self.pVersion = [tempRoot objectForKey:#"PSignature"];
self.pVersion = [tempRoot objectForKey:#"PVersion"];
self.rNumber = [tempRoot objectForKey:#"RNumber"];
self.dVReturned = [tempRoot objectForKey:#"DVReturned"];
cacheValue = [tempRoot objectForKey:#"Cache Value"];
}
}
return self;
}
- (void) saveData:(NSString *)methodName pSignature:(NSString *)TemppSignature pVersion:(NSNumber *)TemppVersion rNumber:(NSNumber *)TemprNumber dVReturned:(NSNumber *)TempdvReturned cacheValue:(NSNumber *)cValue
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields
self.pSignature = TemppSignature;
self.pVersion = TemppVersion;
self.rNumber = TemprNumber;
self.dVReturned = TempdVReturned;
//This checks with methodName I would like to save the value from
//The reason for this is there are 3 different cache values I get at seperate times to save This if statment just makes sure they are in the correct order.
if ([methodName isEqualToString:#"Getmanu"]) {
self.manu = cValue;
} else if ([methodName isEqualToString:#"GetMod"]) {
self.mod = cValue;
} else if ([methodName isEqualToString:#"GetSubMod"]) {
self.submod = cValue;
}
//Set up cacheValue Dictionary that will be added to the property list root dictionary
self.cacheValue = [NSDictionary dictionaryWithObjectsAndKeys:
manu, #"Manu",
mod, #"Mod",
subMod, #"SubMod", nil];
//Pass appropriate values into plist
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
protocolSignature, #"Protocol Signature",
pVersion, #"Protocol Version",
rNumber, #"Request Number",
dVReturned, #"Data Version returned",
cacheValue, #"Cache Value", nil];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData) {
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
} else {
NSLog(#"Error in saveData: %#", error);
}
}
#end
The Problem: nil Values
Here's a problem: pVersion is assigned twice, while pSignature remains nil.
self.pVersion = [tempRoot objectForKey:#"PSignature"];
self.pVersion = [tempRoot objectForKey:#"PVersion"];
This should probably be
self.pSignature = [tempRoot objectForKey:#"PSignature"];
self.pVersion = [tempRoot objectForKey:#"PVersion"];
This in turn leads to trouble at saving time:
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
pSignature, #"PSignature",
pVersion, #"PVersion",
rNumber, #"RNumber",
dVReturned, #"DVReturned",
cacheValue, #"Cache Value", nil];
This constructor doesn't know how many arguments it has, it just keeps going until it hits nil. If pSignature is nil, it will save an empty dictionary.
The Fix: Robust NSDictionary Creation
While the immediate solution is to fix the pSignature reading, it's always fragile to construct dictionaries using dictionaryWithObjectsAndKeys:. It's much better to store each object individually, using setObject:forKey. This throws an exception if the object is nil, so test before calling.
NSMutableDictionary *plistDict = [NSMutableDictionary dictionary];
if (pSignature) [plistDict setObject:pSignature forKey:#"PSignature"];
if (pVersion) [plistDict setObject:pVersion forKey:#"PVersion"];
// ... and so on.
The Alternative: NSCoding
Another option for storage is to serialize. See the Archives and Serializations Programming Guide. This has tradeoffs: while saving and loading is simpler when it works, the resulting files are less human-readable.
Debugging
Finally, three places to put log statements, breakpoints or dialog boxes that help in cases like this:
Log the dictionaries being loaded and the dictionaries being saved. If you open your app and trigger a save immediately, are the dictionaries identical?
Check the first argument sent to nil-terminated methods like arrayWithObjects: and dictionaryWithObjectsAndKeys:
On the simulator, log the path used for saving and open it in Finder. Is there a file there? If it's a property list, you can open it in XCode and see if it has the content you expect.

How to call appdelegete method in different class in iphone

I have a database with 10 tables. As I need to access this database in different view controllers, I have to declare the two methods shown below in each of them. Is there a way I can avoid this by declaring these methods in the application delegate. If yes, how can I go about using these methods in different classes.
- (NSString *) getWritableDBPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:DATABASE_NAME];
}
-(void)createEditableCopyOfDatabaseIfNeeded
{
// Testing for existence
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:DATABASE_NAME];
NSLog(#"%#",writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success)
return;
// The writable database does not exist, so copy the default to
// the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:DATABASE_NAME];
success = [fileManager copyItemAtPath:defaultDBPath
toPath:writableDBPath
error:&error];
if(!success)
{
NSAssert1(0,#"Failed to create writable database file with Message : '%#'.",
[error localizedDescription]);
}
}
in your view controller first of all create a delegate variable
YourAppDelegate *appDelegate=(YourAppDelegate *)[[UIApplication sharedApplication]delegate];
then u can call any methods that you have define in your delegate
like [appDelegate methodName];
This just screams to be implemented as a separate controller with class level methods. I would highly recommend creating a Database controller with a definition like so:
#interface DatabaseController: NSObject
+ (NSString *) getWritableDBPath ;
+ (void) createEditableCopyOfDatabaseIfNeeded ;
#end
Then in your code using it as so:
#import "DatabaseController.h"
NSString * somePath = [DatabaseController getWritableDBPath];
[DatabaseController createEditableCopyOfDatabaseIfNeeded];
Set them as public, so you can call them with [ ]
You just need to change the minus for +
+(void)createEditableCopyOfDatabaseIfNeeded;
You will need to define a protocol for this class and add a variable of that protocol to the member variable of this class as follows:
The classes where the object is created can either call this method using the object. The Best option is to use the app delegate class to implement these methods.
You can then assign the objects's delegate as the app delegate and call the methods.
#protocol mySqlDelegate ;
#interface mySqlClass {
id <mySqlDelegate> delegate;
}
#property (nonatomic, assign) id <mySqlDelegate> delegate;
#end
#protocol mySqlDelegate
- (void) delegateMethodsForThisClass;
#end
first create a common instance for appdelegate.
otherwise in constant.h file create a instance like
mAppDelegate=(YourAppDelegate*)[[UIApplication sharedApplication] ];
then just import constant.h and you may use mAppdelegate anywhere so using this you easily call

Correct way to save/serialize custom objects in iOS

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.
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];
//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.
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.
I would like to share my improvements to Kostas solution, if somebody needs them.
A class name can be used to generate text file name to store object.
It is a good solution to save objects of a view controller in viewWillDisappear method and restore them in viewDidLoad method.
File name should be generated in separate method to avoid duplicate code.
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;
}

NSString read/write to file

Im just having trouble reading and writing NSString to a dat file on the iphone.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *saveFile = [documentsDirectory stringByAppendingPathComponent:#"Profiles.dat"];
Boolean saveFileExists = [[NSFileManager defaultManager] fileExistsAtPath:saveFile];
// NSString *saveFile = [documentsDirectory stringByAppendingPathComponent:#"Profiles.dat"];
NSMutableData *gameData;
NSKeyedUnarchiver *decoder;
NSKeyedArchiver *encoder;
//decoder = [[NSKeyedUnarchiver alloc] initForWritingWithMutableData:gameData];
myGameState *gameState = [myGameState sharedMySingleton];
if(saveFileExists) {
gameData = [NSData dataWithContentsOfFile:saveFile];
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:gameData];
gameState.Profile1 = [decoder decodeObjectForKey:#"Name1"];
gameState.Profile2 = [decoder decodeObjectForKey:#"Name2"];
gameState.Profile3 = [decoder decodeObjectForKey:#"Name3"];
gameState.Profile4 = [decoder decodeObjectForKey:#"Name4"];
gameState.Profile5 = [decoder decodeObjectForKey:#"Name5"];
gameState.curProfile = [decoder decodeObjectForKey:#"curProfile"];
[decoder release];
}
else {
gameData = [NSMutableData data];
encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:gameData];
gameState.Profile1 = #"Default1";
gameState.Profile2 = #"Default2";
gameState.Profile3 = #"Default3";
gameState.Profile4 = #"Default4";
gameState.Profile5 = #"Default5";
[encoder encodeObject:#"Default1" forKey:#"Name1"];
[encoder encodeObject:#"Default2" forKey:#"Name2"];
[encoder encodeObject:#"Default3" forKey:#"Name3"];
[encoder encodeObject:#"Default4" forKey:#"Name4"];
[encoder encodeObject:#"Default5" forKey:#"Name5"];
gameState.curProfile = gameState.Profile1;
[encoder encodeObject:gameState.curProfile forKey:#"curProfile"];
[encoder finishEncoding];
//[gameData writeToFile:<#(NSString *)path#> atomically:<#(BOOL)useAuxiliaryFile#>
[gameData writeToFile:saveFile atomically:YES ];
//[gameData writeToFile:saveFile atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
[encoder release];
}
gameState.curProfile etc are all NSString pointers. Now im very new to Objective C and just cant seem to get this working correctly. I have searched around forums and cant seem to find the correct way to do this. Literally all i want is to read a write a NSString to a dat file but it aint working as is. I have managed to write ints and bools with no problems just cant get past the text problem on my own.
any help is appreciated
thanks
g
Your code works without any problem here. I guess you have problems in other parts of codes.
By the way, if you only need to save a few entries, you don't have to do all these works yourself; just use NSUserDefaults. It automatically prepares the file to save the data, encodes the data, and decodes the data on the next launch. See this Apple document. It can be used as
[[NSUserDefaults standardUserDefaults] setString:#"boo" forKey:#"key!"];
and later the data can be read as
NSString* s=[[NSUserDefaults standardUserDefaults] stringForKey:#"key!"];
It's as easy as this! You might want to call
[[NSUserDefaults standardUserDefaults] synchronize];
to force the saving of the data to the file; usually this isn't necessary (The system does this automatically when quit, etc.)