I have a dictionary that is written into a file after archiving it [NSKeyedArchiver archivedDataWithRootObject:dictionary] . I fetch the file contents and transferring the archived data to my peer devices through multipeer. Then, in my peer device, I am unarchiving the data using [NSKeyedUnarchiver unarchiveObjectWithData:data]. But, it is returning nil while unarchiving it, though data is present in it.
I am suspecting that the content is huge in file. Is there any alternative for NSkeyedArchiver/ NSKeyedUnarchiver ?
Code:
Archiving:
[[NSKeyedArchiver archivedDataWithRootObject:dictionary] writeToFile:fileAtPath options:NSDataWritingAtomic error:&error];
Transferring:
[NSData dataWithContentsOfFile:fileAtPath] ;
Unarchiving:
[NSKeyedUnarchiver unarchiveObjectWithData:data];
Try to write data like below,
[data writeToFile:filePath atomically:NO];
here data means [NSKeyedArchiver archivedDataWithRootObject:dictionary] your archived data.
otherwise archived and unarchived method is fine.
Hope this will help :)
Related
How can one wait for a plist write operation to complete before performing other operations?
// Write plist file
NSString *pathToPlist = [NSString pathWithComponents:#[[Util documentsDirectoryPath], #"Settings.plist"]];
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithContentsOfFile:pathToDocumentsPlist];
[dic setObject:#"SomeObject" #"SomeKey"];
[dic writeToFile:pathToDocumentsPlist atomically:YES];
// Read plist file
NSDictionary *updatedDictionary = [NSDictionary dictionaryWithContentsOfFile: pathToPlist];
In this case, I would like the updatedDictionary to have value of the updated plist, however the plist write method (writeToFile) seems to be asynchronous and takes an amount of time to complete. As a result, updatedDictionary tends to read in the old, instead of the updated configuration, since by the time of the plist read, the save operation was not completed.
How can one resolve this issue? Just a pure speculation, could setting atomically:NO possibly help? Thanks!
First point this method is not asynchronous, it is synchronous, it returns a boolean when the operation succeeds or not, so you are probably not checking if it was saved, and probably this is your problem. Check the return of the writeToFile:atomically:!
Now, a really simple question, why you are doing this?
this is your code:
// Write plist file
NSString *pathToPlist = [NSString pathWithComponents:#[[Util documentsDirectoryPath], #"Settings.plist"]];
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithContentsOfFile:pathToDocumentsPlist];
[dic setObject:#"SomeObject" #"SomeKey"];
[dic writeToFile:pathToDocumentsPlist atomically:YES];
// Read plist file
NSDictionary *updatedDictionary = [NSDictionary dictionaryWithContentsOfFile: pathToPlist];
First you are reading the plist to a dictionary - OK
Second you are updating the data in the dictionary - OK
Third you are saving the dictionary to the plist again - OK
Fourth you are getting the plist again - WHY?
You already have the content of the plist in your dictionary, as you updated the plist using "dic", so why you need to read the same again if it will have exactly the same content of the plist?? dic and plist are the same at that point of your code.
if you want to make sure that the content was saved,the method writeToFile:atomically returns a boolean where you can see if the operation succeeds or not. By the way, if this method returns a boolean saying that the operation was complete, the method cannot be asynchronous, to be asynchronous it should use block not a return value.
About your question with the "atomically", no, it will not make it synchronous or asynchronous, describing it in the simplest way, this atomically means that "no one" can access the content of this file until it is completely saved, so it will save in some temporary place, when it is complete, it will move to the final destination, so if some object try to read/update/delete it before it is completely saved, it will not be possible.
at the end try this to write your plist:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
if([dic writeToFile:[documentsDirectory stringByAppendingString:#"/Settings.plist"] atomically:YES])
//it worked
cheers,
Roberto
This is a iOS 101 question and I feel like an ass for not knowing the answer :-)
The question is - how do I reset an iOS app to the state it was when first installed?
The app stores content in NSUserDefaults and plists. It appears excessive to have to ask the user to delete and reinstall the app.
What are the possible options available aside from asking my user to simply reinstall the app (which would makes me look kinda un-innovative :-) )
This particular app used NSUserDefaults and plists to store data. But while you come up with your creative solutions I'd appreciate any clues for other situations as well (i.e. where data is in core data and .plists)
Also if this is a feature needs to be implemented in the app code, appreciate suggestions on a elegant solution for it.
Thanks!
Delete all files from the sandbox:
NSString *p = [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent];
for (NSString *fname in #[ #"tmp", #"Library", #"Documents", #"Caches", #"Preferences" ]) {
NSString *path = [p stringByAppendingPathComponent:fname];
[[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
}
Alternative way for resetting the user defaults only:
NSDictionary *dRepr = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
for (NSString *key in dRepr) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
[[NSUserDefaults standardUserDefaults] synchronize];
[NSUserDefaults resetStandardUserDefaults];
The NSUserDefaults is a keyValue collection so for all the keys that you use across the app you can call this method:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"yourKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
about plist I suppose that you are talking about writing NSArrays and NSDictionaries on the disk's device or implementing NSCoding delegate in your objects subclasses and archiving them... in this case just remove the files using the file manager
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:#"filePath" error:&error];
for core data it's easy, you can delete the sqlite file like the plists, don't forget to re initialize the persistent store, managed object context etc... after this operation
In my application, I have a dictionary called matchDataDictionary and I set the following:
[[GCTurnBasedMatchHelper sharedInstance].matchDataDictionary setObject:deck forKey:#"deck"];
where deck is an instance of Deck: https://gist.github.com/naderhen/4711899
I then am trying to just test the archiving/unarchiving with the following:
Archiving (this seems to work):
NSMutableData *newData = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:newData];
[archiver encodeObject:[GCTurnBasedMatchHelper sharedInstance].matchDataDictionary forKey:#"root"];
[archiver finishEncoding];
NSLog(#"Archived Data: %#", newData);
Unarchiving (Doesn't Work):
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:newData];
NSMutableDictionary *matchDataDictionary = [unarchiver decodeObjectForKey:#"root"];
[unarchiver finishDecoding];
NSLog(#"Unarchived Data: %#", matchDataDictionary);
Any guidance as to how I can go about effectively archiving and subsequently unarchiving this data would be greatly appreciated. If it helps, this is for a turn-based game using the GameKit framework, so I'm trying to prepare user actions to send via the current match's matchData.
Also! Any advice on how I might fix up the Deck encoding to include its "Tiles" array would be greatly appreciated.
Thanks so much!
Nader
Use the NSCoding protocol. Each entity encodes itself, and then decodes itself. Kind of magical how easy it is, and as the app grows, people can add the functionality to the new parts of the object graph without knowing anything about the archivers/unarchivers, or their formats.
NSCoding Protocol
I have an open source project on GitHub that makes it easy to do NSCoding to/from JSON: JSONCoding.
Here is some code from before I developed that (out of a unit test):
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:firstDish];
RestaurantDish *retrievedDish = [NSKeyedUnarchiver unarchiveObjectWithData:data];
assertThat(retrievedDish.name, is(dishName));
The instance firstDish is just an entity Dish that represents a plate of food.
How do I get image data out of JSON returned by a WCF Data Service?
I've got a WCF Data Service serving some objects that include some binary data that is an image (column type in SQL Server is image). It comes across as text gibberish in the JSON. In the iOS client I'm writing for this service, I want to display this data as an image ([UIImage imageWitData:myData]).
Here is what I'm doing:
NSData *networkData = nil; //assume this magically gets data from the service
NSError *err = nil;
//assume the returned JSON has one object and is not an array
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:networkData
options:NSJSONReadingAllowFragments error:&err];
//handle error if needed
NSString *imageString = [dict objectForKey:#"Image"];
NSData *imageData = [imageString dataUsingEncoding:NSUTF8StringEncoding];
///The service is using UTF-8.
UIImage *image = [UIImage imageWithData:imageData];
Putting that image in a UIImageView doesn't show anything. What am I doing wrong?
Turns out that binary data returned from WCF Data Services is base64 encoded. I'm using the NSData-Base64 category to parse it, and it's working great. So the solution looks like this:
NSData imageData = [NSData dataFromBase64String:imageString];
When I imported those files into my project ARC threw up some errors, but I was able to solve them by removing a couple of autorelease calls in the .m file.
I'd like my app to decompress and handle the contents of zip files.
I'm trying to do this the Cocoa way, without using external libraries, so I'm turning to NSKeyedUnarchiver and here's my code:
-(void) unarchive{
NSString *outputDirectory = [[NSHomeDirectory() stringByAppendingString:#"/Documents/"] stringByAppendingString:#"TheNewFolderName/"];
NSLog(#"Output Directory: %#", outputDirectory);
//MyArchive.zip is the filename of the zip file I placed in Xcode
NSData *theArchive = [NSKeyedUnarchiver unarchiveObjectWithFile:#"MyArchive.zip"];
NSError *error;
[theArchive writeToFile:outputDirectory options:NSDataWritingAtomic error:&error];
NSLog(#"theArchive is %#", theArchive);
And this prints "theArchive is null"! What am I doing wrong..?
NSKeyedArchiver and NSKeyedUnarchiver are used for archiving and unarchiving objects (NSObject subclasses, with certain restrictions) in your application. I'm afraid you cannot use it with zip files.