Errors with Archiving / Unarchiving NSMutableDictionary - ios

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.

Related

Alternative for NSKeyedArchiver/ NSKeyedUnarchiver iOS

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 :)

Playing video from NSData stored in Core Data

I am trying to play a video stored in Core data. After fetch it shows, there is an object and objects.video returns a value but the dataString prints out to be null. I am not sure what I maybe doing wrong. Is it a right way to play video or is there something I could have done better?
I have single object in Core Data.
I have stored as video as NSData in Core data. I want to get that stored video and play. Is there any other way I can do it?
_context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Activity" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
// Specify how the fetched objects should be sorted
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"level"
ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSError *error = nil;
NSArray *fetchResults = [_context executeFetchRequest : fetchRequest error : &error];
if(fetchRequest == nil){
NSLog(#"Nothing fetched");
}
for (Activity *objects in fetchResults){
NSLog(#"%#",objects.video);
prints-> External Data Reference: <self = 0x7bf48750 ; path = FF54B18E-10B3-4B04-81D4-55AC5E2141B9 ; length = 504426>
NSString *dataString = [[NSString alloc] initWithData:objects.video encoding:NSUTF8StringEncoding];
NSLog(#"%#",dataString);
NSURL *movieURL = [NSURL URLWithString:dataString];
NSLog(#"%#",movieURL);
_moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
[_moviePlayer.view setFrame:CGRectMake (20, 20, 200 , self.view.bounds.size.height/2)];
[self.view addSubview:_moviePlayer.view];
[_moviePlayer play];
}
NSLog(#"%i",fetchResults.count);
It will just be a binary property on the managed object. You call the property and it returns NSData. From there you can do whatever you want with the NSData while it is memory. Your problem is that you can't convert the NSData into a NSURL. A NSURL is a reference to data where NSData is the actual data.
What you need to do is store the video file on disk, outside of the SQLite file. Then store a reference to it (aka a url) in Core Data. That will allow you to use the video file with the movie player.
As others have said, storing the video in the SQLite file is a bad idea. It will wreck the performance of Core Data.
Update 1
Thanks. I have not saved the video directly to core data instead just copied the video url and saved it as string in core data. Should I create a different folder for the app to store the videos whenever I create a copy of the videos the users use so even if they delete the original video that was uploaded to core data, the copy remains intact and thus maintaining integrity of the Objects in Core Data.
Your comment is unclear. According to your question, you are storing the actual video in Core Data. Based on the output you showed, you stored the file in Core Data. Have you changed that? If so, you should store the video file in a known location and store the relative URL to that location in core data as a string. Then you build the full URL from that when you are ready to use it. Since the sandbox can change you can't store the entire URL as it will go stale.

Archiving on iOS using NSKeyedArchiver

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.

iOS - ASIHTTPRequest - Trying to parse through responseString

I've spent about 16 hours researching and attempting different code changes, but cannot figure this one out. I have an iOS app that consumes a website using ASIHTTPrequest:
-(void)refresh{
NSURL *url = [NSURL URLWithString#"http://undignified.podbean.com/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSString *response = [request responseString];
NSLog(#"%#",response];
}
The above code returns the website source and spits it out to the console via NSLog. The goal I'm trying to achieve from this is search through the 'responseString' for URLs ending in *.mp3, load those into an array and finally load the mp3 URLs into a UITableView.
To summarize:
Consuming website data with ASIHTTPRequest
Trying to search through the responseString for all links that have *.mp3 extensions and load them into an array.
Add parsed links to UITableView.
I think at this junction I have attempted too many things to make any sound judgements at this point. Any suggestions or nudges in the right direction would be greatly appreciative.
Below is an example of the response (HTML). Please note this is only a snippet as the entire HTML source is rather large, but this includes a section to the mp3 files:
a href="http://undignified.podbean.com/mf/web/ih2x8r/UndignifiedShow01.mp3" target="new"><img src="http://www.podbean.com/wp-content/plugins/podpress/images/audio_mp3_button.png" border="0" align="top" class="podPress_imgicon" alt="icon for podbean" /></a> Standard Podcasts [00:40:24m]: <span id="podPressPlayerSpace_2649518_label_mp3Player_2649518_0">Play Now</span> | Play in Popup | Download | Embeddable Player | <a>Hits (214)</a><br/
I really don't like answers that say "Just don't do it that way." But... Just don't do it that way.
It is possible to extract all of the links to mp3s in the html from the address you posted. But this is almost always the wrong way to approach things, and this case is no exception.
Essentially what it seems you are trying to do is create a podcaster client. You should give some thought as to how others have handled this type of use case before. Generally a podcast will have an associated RSS feed that outlines exactly the data you are looking for, again your podcast is no exception. If one simply navigates to the link supplied in your question and then looks around the page for either "subscribe to podcast" or "RSS" they will find the link that leeds to the RSS feed: http://undignified.podbean.com/feed/. This address leads to the XML which contains the items of the podcasts.
This document, unlike the document returned by your original address, is valid XML meaning it can be parsed with an NSXMLParser. NSXMLParser is very powerful & flexible. But a little hard to get started with. Here is some sample code for an NSXMLParser subclass which acts as it's own delegate.
UnDigParser.h
#import <Foundation/Foundation.h>
#interface UnDigParser : NSXMLParser <NSXMLParserDelegate>
#property (readonly) NSArray *links;
#end
UnDigParser.m
#import "UnDigParser.h"
#implementation UnDigParser{
NSMutableArray *_links;
}
#synthesize links = _links;
-(void)parserDidStartDocument:(NSXMLParser *)parser{
_links = [[NSMutableArray alloc] init];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"enclosure"]){
NSString *link = [attributeDict objectForKey:#"url"];
if (link){
[_links addObject:link];
}
}
}
-(BOOL)parse{
self.delegate = self;
return [super parse];
}
#end
This code can be tested like so:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:#"http://undignified.podbean.com/feed/"];
// This is a sync call thus the background thread
UnDigParser *parser = [[UnDigParser alloc] initWithContentsOfURL:url];
[parser parse];
NSLog(#"links:%#",parser.links);
});
The logged output was:
links:(
"http://undignified.podbean.com/mf/feed/cfdayc/UndignifiedShow03.mp3",
"http://undignified.podbean.com/mf/feed/wbbpjw/UndignifiedShow02Final.mp3",
"http://undignified.podbean.com/mf/feed/ih2x8r/UndignifiedShow01.mp3",
"http://undignified.podbean.com/mf/feed/t4x54d/UndignifiedShow00.mp3"
)
That should be enough to get you started.
I would do this in two steps:
Get all href values
Use regular expressions to check for a valid url ending in .mp3
BTW, I think ASIHTTPRequest is getting outdated. And if you only use it to fetch HTML, I suggest you look into the build in methods from the iOS framework to do just that.
Using requestDidReceiveResponseHeadersSelector and get file name from header, trying print all key and value from NSDictionary header.

NSMutableArray writeToUrl

Is it possible to use the:
[NSMutableArray writeToURL:(NSString *)path atomically:(BOOL)AuxSomething];
In order to send a file (NSMutableArray) XML file to a url, and update the url to contain that file?
for example:
I have an array and I want to upload it to a specific URL and the next time the app launches I want to download that array.
NSMutableArray *arrayToWrite = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",nil];
[arrayToWrite writeToURL:
[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"] atomically:YES];
And at runtime:
NSMutableArray *arrayToRead =
[[NSMutableArray alloc] initWithContentsOfURL:[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"]];
Meaning, I want to write an NSMutableArray to a URL, which is on a web hosting service (e.g. batcave.net, the URL receives the information and updates server sided files accordingly.
A highscore like setup, user sends his scores, the server updates it's files, other users download the highscores at runtime.
As for part one of your question,
I'll assume you want to use the contents of a NSMutableArray to form some sort of a URL request (like POST) that you will send to your web service and expect back some information...
There is no prebuilt way of sending the contents of a NSMutableArray to an URL but there are simple ways of doing this yourself. For example, you can loop through the data of your array and make use of NSURLRequest to create a URL request that complies with the interface of your web service. Once you've constructed your request you can send it by passing it a NSURLConnection object.
Consider this very simple and incomplete example of what the client-side code might look like using an Obj-C array to provide data...
NSMutableData *dataReceived; // Assume exists and is initialized
NSURLConnection *myConnection;
- (void)startRequest{
NSLog(#"Start");
NSString *baseURLAddress = #"http://en.wikipedia.org/wiki/";
// This is the array we'll use to help make the URL request
NSArray *names = [NSArray arrayWithObjects: #"Jonny_Appleseed",nil];
NSString *completeURLAsString = [baseURLAddress stringByAppendingString: [names objectAtIndex:0]];
//NSURLRequest needs a NSURL Object
NSURL *completeURL = [NSURL URLWithString: completeURLAsString];
NSURLRequest *myURLRequest = [NSURLRequest requestWithURL: completeURL];
// self is the delegate, this means that this object will hanlde
// call-backs as the data transmission from the web server progresses
myConnection = [[NSURLConnection alloc] initWithRequest:myURLRequest delegate: self startImmediately:YES];
}
// This is called automatically when there is new data from the web server,
// we collect the server response and save it
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Got some");
[dataReceived appendData: data];
}
// This is called automatically when transmission of data is complete
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// You now have whatever the server sent...
}
To tackle part 2 of your question, the receiver of a web request will likely require some scripting or infrastructure to make a useful response.
Here, Answer in this question:
Creating a highscore like system, iPhone side
I couldn't edit my post because I posted from my iPhone as an anonymous user, sorry.

Resources