NSKeyedArchiver file - losing data - iphone

Using Iphone SDK 3.1.2. I am saving some info to a file using NSKeyedArchiver object. I have 4 keys.
I read data from the file for all 4 keys on applicationDidFinshLaunching and write back to the file (all 4 keys) on applicationDidTerminate. Also when the user saves any info i write the data for the particular key its for.
In case there is no file initially i allocate an array for the accountlist, this allows me to add accounts to it.
I am having issues with the file periodically losing the data saved to it. Sometimes its one key affected ie accountlist, sometimes it is the favorites list. It seems to be exacerbated if i just save 1key as opposed to all 4. ive viewed the file using organsier
and indeed the data is missing. The thing is many times it does not work. I don't why its so inconsistent. Also if i termintate the debugger in xcode i would expect to only lose recent changes not the whole files data whihc does seem to happen. The whole NSKeyedArchiv er thing seems so flakey im considering maybe another persistent storage strategy. Anyone come across anything like this.
- (void)viewDidLoad
{
if( self.accountList == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
self.accountList = array;
[array release];
}
if( self.phoneSettings == nil)
{
PhoneSettings* mySettings = [[PhoneSettings alloc] initSettings];
self.phoneSettings = mySettings;
[mySettings release];
}
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *archiveArray = [unarchiver decodeObjectForKey:kDataKey];
if(archiveArray != nil)
self.accountList = [archiveArray retain];
else
LOG("No archived accounts" );
NSMutableArray *archiveCallList = [unarchiver decodeObjectForKey:kCallListKey];
if(archiveCallList !=nil)
appDelegate.callList = [archiveCallList retain];
NSMutableArray *archiveFavouritesList = [unarchiver decodeObjectForKey:kfavouritesKey];
if(archiveFavouritesList != nil)
appDelegate.favouritesList = [archiveFavouritesList retain];
PhoneSettings* archiveSettings = [unarchiver decodeObjectForKey:kPhoneSettings];
if (archiveSettings != nil)
self.phoneSettings = [archiveSettings retain];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
then in
- (void)applicationWillTerminate:(NSNotification *)notification {
LOG("Application terminating");
SessionTalkAppDelegate* appDelegate = (SessionTalkAppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:self.accountList forKey:kDataKey];
[archiver encodeObject:appDelegate.callList forKey:kCallListKey];
[archiver encodeObject:appDelegate.favouritesList forKey:kfavouritesKey];
[archiver encodeObject:self.phoneSettings forKey:kPhoneSettings];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}

You don't need that much code. Save your data:
NSMutableDictionary *appState = [NSMutableDictionary dictionary];
[appState setObject: self.accountList forKey: kDataKey];
[appState setObject: appDelegate.callList forKey: kCallListKey];
[appState setObject: self.phoneSettings forKey: kPhoneSettings];
if ( [NSKeyedArchiver archiveRootObject: appState toFile: [self dataFilePath]] == NO )
NSLog(#"Failed to archive %#", appState);
Read the data:
NSMutableDictionary *appState = [NSKeyedUnarchiver unarchiveObjectWithFile: [self dataFilePath]];
Note that NSKeyedUnarchiver can raise an NSInvalidArgumentException if the file does not contain a valid archive, so you should enclose the call in a #try{} block.

Related

Change AVMetadataItem

I have some files, -m4a -mp4 -mp3 etc.
I want to change these details
AVMetadataItem
AVMetadataCommonKeyArtwork
AVMetadataCommonKeyArtist
I can do with AVAssetExportSession, But I need to change the directory. Is there a way I can write directly on the file please?
I found this program, but does not work :(
NSError *error;
AVAssetWriter *assetWrtr = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:self.path] fileType:AVFileTypeAppleM4A error:&error];
NSLog(#"%#",error);
NSArray *existingMetadataArray = assetWrtr.metadata;
NSMutableArray *newMetadataArray = nil;
if (existingMetadataArray)
{
newMetadataArray = [existingMetadataArray mutableCopy]; // To prevent overriding of existing metadata
}
else
{
newMetadataArray = [[NSMutableArray alloc] init];
}
AVMutableMetadataItem *item = [[AVMutableMetadataItem alloc] init];
item.keySpace = AVMetadataKeySpaceCommon;
item.key = AVMetadataCommonKeyArtwork;
item.value = UIImagePNGRepresentation([[UIImage alloc] initWithContentsOfFile:[[NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#".image"]]);
[newMetadataArray addObject:item];
assetWrtr.metadata = newMetadataArray;
[assetWrtr startWriting];
[assetWrtr startSessionAtSourceTime:kCMTimeZero];
change UIImagePNGRepresentation to UIImageJPEGRepresentation you need jpeg data not png.

Error archiving two arrays in objective-c

I'm going through the Beginning iOS 5 example and trying to tweak it for my usage. I have a DataManager singleton object that has properties of:
#property (nonatomic, strong) NSArray *frequencyArray;
#property (nonatomic, strong) NSMutableArray *networkArray;
As I'm using ARC, my singleton looks like:
- (id)init {
if (self = [super init]) {
_networkArray = [[NSMutableArray alloc] init];
}
return self;
}
+ (id)sharedInstance {
if (sharedDmgr == nil) {
sharedDmgr = [[super allocWithZone:NULL] init];
}
return sharedDmgr;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
I followed the example code to conform this class to NSCoding and NSCopying. I created two methods in this class to archive itself, and unarchive itself. Those are:
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingFormat:ARCHIVE_FILE_NAME];
}
- (void)archiveAppData {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self forKey:ARCHIVE_DATA];
// [archiver encodeObject:_frequencyArray forKey:DMGR_FREQUENCY_ARRAY];
// [archiver encodeObject:_networkArray forKey:DMGR_NETWORK_ARRAY];
[archiver finishEncoding];
// BOOL success = [data writeToFile:[self dataFilePath] atomically:YES];
NSError *error;
BOOL success = [data writeToFile:[self dataFilePath] options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Could not write file %#", [error description]);
}
}
- (void)unarchiveAppData {
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
sharedDmgr = [unarchiver decodeObjectForKey:ARCHIVE_DATA];
// self.frequencyArray = [unarchiver decodeObjectForKey:DMGR_FREQUENCY_ARRAY];
// self.networkArray = [unarchiver decodeObjectForKey:DMGR_NETWORK_ARRAY];
[unarchiver finishDecoding];
}
I get the error:
Could not write file Error Domain=NSCocoaErrorDomain Code=513 "The
operation couldn’t be completed. (Cocoa error 513.) "The operation
couldn’t be completed. Operation not permitted"}
which I googled and saw that it's referring to modifying the bundle. But I don't see where I'm doing that since I thought I was writing to the documents directoroy with my [self dataFilePath] code.
When my application resigns active, I call the archiveAppData method. When my first viewController loads, I call the unarchiveAppData. As you can see from my commented out code, I tried to just encode those two arrays instead, and then decode those as opposed to the DataManager object itself, but that didn't seem to work either. Any thoughts on what I'm doing wrong? Thanks.

can any one help me in appending the data to the plist file?

I have tried this code to copy my data to plist but its not working in the case of appending .can any one suggest me a good method for appending the data?
NSMutableDictionary *nameDictionary = [NSMutableDictionary dictionary];
NSString *plistPath=[[NSBundle mainBundle] pathForResource:#"listNew" ofType:#"plist"];
[nameDictionary setValue:nameEntered.text forKey:#"name"];
[nameDictionary setValue:company.text forKey:#"company"];
[nameDictionary setValue:designation.text forKey:#"designation"];
// create dictionary with values in UITextFields
NSMutableArray *plist = [NSMutableArray arrayWithContentsOfFile:plistPath];
if (plist == nil) plist = [NSMutableArray array];
[plist addObject:nameDictionary];
[plist writeToFile:plistPath atomically:YES];
UIAlertView *tellErr = [[UIAlertView alloc] initWithTitle:title message:#"succsefully created" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[tellErr show];
nameEntered.text=#"";
company.text=#"";
designation.text=#"";
You should use +[NSKeyedArchiver archiveRootObject:toFile:] to archive the Array object to file. And use +[NSKeyedUnarchiver unarchiveObjectWithFile:] to unarchive it.
You can't write to the application main bundle in iOS. You must copy the property list file to a writable directory; then you can read it, modify it, and rewrite it.
Ok dude, here is the code that you need. I am using this in an existing iOS program
first check to see if there is any info in the dictionary to begin with
static BOOL dicIsNULL = YES;
....
//initialize the variable
dicIsNULL = YES;
//READ FILE
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *stringsPlistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"listNew.plist"];
dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:stringsPlistPath];
//Check if this dic has values
for (id key in dictionary)
{
//NSLog(#"listNew.plist file has values");
dicIsNULL = NO;
break;
}
Now do the following depending upon the dictonary had already been created or not. Add your values in the dictionary, this is just a sample code.
if (dicIsNULL == YES)
{
//NSLog(#"dictionary is NULL");
dictionary = [[NSMutableDictionary alloc] init];
dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:tempArray, infoKeyStr, textTitleField1.text, titleKeyStr, userSelectedCatStr, catKeyStr, nil];
}
if (dicIsNULL == NO)
{
//NSLog(#"dictionary is NOT null");
dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:stringsPlistPath];
[dictionary setObject: tempArray forKey: infoKeyStr];
[dictionary setObject: textTitleField1.text forKey: titleKeyStr];
[dictionary setObject: userSelectedCatStr forKey: catKeyStr];
}
hope this helps you out.
I would go for:
NSString *plistPath=[[NSBundle mainBundle] pathForResource:#"listNew" ofType:#"plist"];
NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
if (plist == nil) plist = [[NSMutableDictionary alloc] init];
[plist setObject:nameEntered.text forKey:#"name"];
[plist setObject:company.text forKey:#"company"];
[plist setObject:designation.text forKey:#"designation"];
[plist writeToFile:plistPath atomically:YES];
... etc ...
Of course, this depends on what exactly you want to achieve.

Cannot figure out why my app crashes when I use NSKeyedArchivers / NSKeyedUnarchivers

I am developing my first iphone 'Diary' app, which uses custom 'Entry' objects that hold an NSString title, NSString text and NSDate creationDate. When I try to archive an NSMutableArray of Entry objects, and later retrieve them the next time the view loads, the app crashes. I have gone through a bunch of sample codes and examples that use NSKeyedArchivers, but still couldn't figure out why that happens. I am guessing there is a problem with the initialization of the array that holds the entries but not sure...
Here is the code, maybe you could find something that I have persistently overseen..."
//--------- Entry.m---------------
- (id) initWithCoder:(NSCoder *)aDecoder{
if ((self = [super init])) {
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.title forKey:#"title"];
[aCoder encodeObject:self.text forKey:#"text"];
[aCoder encodeObject:self.created forKey:#"created"];
}
//-------------- Diary View Controller.m
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void) writeDataToArchive {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:self.entriesArray forKey:#"entriesArray"];
[archiver finishEncoding];
BOOL result = [data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}
- (void)addItem:sender {
int count = [entriesArray count] +1;
NSString *newEntryTitle = [NSString stringWithFormat:#"Entry %d", count];
Entry *anEntry = [[Entry alloc] initWithTitle:newEntryTitle text:#"-"
created:[NSDate date]];
[entriesArray addObject:anEntry];
[self.tableView reloadData];
[anEntry release];
[self writeDataToArchive];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *array = [unarchiver decodeObjectForKey:#"entriesArray"];
entriesArray = [array mutableCopy];
[array release];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
// ... some other stuff
NSUInteger row = indexPath.row;
Entry *entry = [entriesArray objectAtIndex:row];
cell.textLabel.text = entry.title;
return cell;
}
Thanks a lot.
When you read an array back out with NSKeyedUnarchivers you always get an unmutable copy back. You would need to declare *array as NSArray or just get rid of array all together.
entriesArray = [[unarchiver decodeObjectForKey:#"entriesArray"] mutableCopy];
And #JeremyP points out another issue. Since you didn't alloc or retain *array you should not release it.
You should not release array in viewDidLoad because you do not own it.
Please review the Cocoa memory management Rules because there are a couple of other memory management issues in your code. In particular,
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
in your initWithCoder: method all leak on the assumption the properties are retain or copy.

Why does NSMutableArray Count crash my app?

I've ran into a problem. Everytime I'am starting my app it crashes. Heres me code.
The Debugger Says: [list count] crashes the app. I have no idea. NSLog(#"%#", self.list); gives me one item as expected...
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
data = [[NSData alloc] initWithContentsOfFile:filePath];
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSMutableArray *array = [unarchiver decodeObjectForKey:#"TOWN"];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
self.list = array;
NSLog(#"%#", self.list);
NSLog(#"count %i", [list count]);
The archive which is opened was created like that:
Adding *adding = [[Adding alloc] init];
adding.nummer = 1;
adding.stadt = stadt.text;
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:adding forKey:#"TOWN"];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
If you need any futher code let me know. I would be very thankfull for any help :)
I believe the problem is that you are encoding the Adding Class here:
[archiver encodeObject:adding forKey:#"TOWN"];
which is not an NSMutableArray yet when you are decoding you are trying to get it back as an NSMutableArray here:
NSMutableArray *array = [unarchiver decodeObjectForKey:#"TOWN"];
And I am guessing your class Adding is not an Array.