- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
xmlParseChunk(context, (const char *)[data bytes], [data length], 0);
}
my Question is as follows
=> didReceiveData: method receives data in parts
Some what like this
first data----------| <masterData>Main</ma
second data-----| ster><masterData>Main2
third data --------| </masterData>
=> How xmlParseChunk() method can parse all these chunks successfully?
Apple's XMLPerformance sample app illustrates a complete implementation of libxml2 integrated with NSURLConnection and chunk parsing. I found it very helpful.
One approach is to have your delegate contain an NSMutableData member and invoke appendData: when you get new data. Then parse it when your delegate gets the connectionDidFinishLoading message.
Related
Currently, the way our app works is we download a text file that describes the information we want. Then we use NSURLConnection and request that one packet that the text file describes. Then we ask for the next packet, wait for it to come in, check it, then ask for the next packet.
Is there a better way to do this without the text file? I feel like I should be able to have the app say, "InformationForJohnDoe" and then the server will just start sending all the packets for JohnDoe, but in this scenario, I don't know how I'd know which data is which in my connectionDidFinishLoading delegate method.
The web service implementation looks like this:
[WebGet(UriTemplate = "GetTestData/{file}")]
public Stream GetTestData(string file)
{
string fileName =
"\\testdirectory" + file;
if (File.Exists(fileName))
{
FileStream stream = File.OpenRead(fileName);
if (WebOperationContext.Current != null)
{
WebOperationContext.Current.OutgoingResponse.ContentType = "text/.txt";
}
return stream;
}
return null;
}
I'm not much of a C# person. I've only just started in C# and web services.
the Idea is that you append to a NSData you define in the class the NSData you get from the
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
method.
Then, in the
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
method, you know that all data is completely loaded and ready for use.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Connection didReceiveData of length: %u", data.length);
[self.dataData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do seomething with self.dataData
}
I use the following three delegate methods to pull images from S3. They are called in the presented order.
-(void)request:(AmazonServiceRequest *)request didReceiveResponse:(NSURLResponse *)response;
-(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data;
-(void)request:(AmazonServiceRequest *)request didCompleteWithResponse:(AmazonServiceResponse *)response;
I've noticed that if an image is large that sometimes the didReceieveResponse will get called before the previous didCompleteWithResponse has finished. This screws up the NSData received in the second delegate.
How do I fix this? All three are getting called in a for statement iteration for each iteration. like so:
for (NSString *name in array){
#try {
S3GetObjectRequest *gor = [[S3GetObjectRequest alloc]initWithKey:name withBucket:[Constants pictureBucket]];
[gor setDelegate:self];
[s3 getObject:gor];
}
I have not worked with S3 but I am sure that there would be some way of distinguishing the three different AmazonServiceRequest *request in the delegate functions(maybe the key you are using to init ). So you can take whatever action you are doing with the NSData by first making a check on the request you are getting back in the delegate functions
I'm attempting to parse validated JSON from a yelp search result.
This correctly spits out the json as expected (confirmed in simulator browser and my own).
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSString *dump = [[[NSString alloc] initWithData: data encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"Did Recieve data: %#", dump);
[JSONData appendData:data];
}
But when my connection finishes loading I'm having a hard time extracting the results and parsing the data:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Connection Did Finish Loading");
NSError *error = nil;
id cureLocations = [[CJSONDeserializer deserializer] deserializeAsDictionary:JSONData error:&error];
[JSONData release];
NSLog(#"Connection finished loading: %#", error);
}
I get: Connection finished loading: Error Domain=CJSONDeserializerErrorDomain Code=-11 "The operation couldn’t be completed. (CJSONDeserializerErrorDomain error -11.)"
I switched to TouchJSON from SBJSON because I wasn't able to extract it from that framework either. I've attempted loading it into Dictionaries and Arrays with null as the result. At this point I've been banging my head on the keyboard for hours and would greatly appreciate any input.
JSON sample
Update:
I am a dummy. I hadn't initialized JSONData. Please accept my apologies for wasting your time and thanks for your suggestions.
SBJSON is a pretty decent and well known parser. If it didn't parse your input, you'd probably assume it's because the input was genuinely bad. If TouchJSON isn't parsing it either, the input is definitely bad. So there's something going on with you JSONData object that's dodgy.
I would suggest you print out your JSON data to the console in your connectionDidFinishLoading method and try re-validating it. See what's actually in the data object you're passing to CJSON.
Ugh, after further review of the application it seems that I rushed to copy my samples into this project and forgot to initialize JSONData:
self.JSONData = [[[NSMutableData alloc] init]autorelease];
Then I updated my didReceiveData method:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[[self JSONData] appendData:data];
}
And everything is now working as expected. This is the second time I've run into this error. I guess I always expected the debugger to pick it up. Thanks for everyones time and assistance.
Is there any way I can convert the value of a [NSData bytes] to a float so that I can add it to a progress bar?
Thanks,
Kevin
In a nutshell: [data length]
Here is the snippet of how the download bar I use works.
// Can get called numerous times during download process
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Accumulate incoming data into mutable data object
[fileData appendData:data];
byteCount += [data length];
float progress = byteCount/(mapToDownload.fileSize);
[self performSelectorOnMainThread:#selector(updateProgress:) withObject:[NSNumber numberWithFloat:progress] waitUntilDone:NO];
}
Let me know if you need more information.
[Added Oct 26 to address your other question:]
I have not worked with NSStream. My example is from an asynchronous NSURLConnection example. Therefore, let's ignore my previous code example.
You mentioned that you have [NSData bytes]. [NSData length] should return you how much data you have. Assuming you know the size to be downloaded then:
float progressPercentage = [yourNSData length]/knownFileSize;
should give you the percentage needed to update the progress bar. You could then set your progress bar:
[yourProgressBar setProgress:progressPercentage];
I've been using NSURLConnection to do a HTTP post to establish the connection. I've also implemented the didReceiveData delegate to process incoming bytes as they become available.
As incoming data comes in via didReceiveData, I add the NSData to a data buffer and try parsing the bytesteam if enough data has come in to complete a message segment. I'm having a hard time managing the data buffer (NSMutableData object) to remove bytes that have been parsed to structs. Was curious if there's an easier way. My didReceiveData delegate is below.
It works, but I don't think I'm managing memory correctly after I copy the message segment (currMsg) out of the responseData buffer and call processMsg. I get double free errors when running under the Simulator -- the program doesn't crash.
NSMutableData/NSData provide methods for appending bytes to the end but I didn't see any methods for removing bytes from the beginning (bytes representing whats already been parsed. I would appreciate some advice on how to best remove the parsed bytes from the responseData buffer. I come from a mostly C background so I'm not sure if there are better ways of manipulating the NSData bytes pointer. I'd like to avoid copying if possible -- just want to process a portion of the responseData buffer and leave the rest in responseData for next time enough bytes are in it for parsing.
Thanks
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSData *tmpBuffer = nil;
NSInteger currMsgSize = 10;
[responseData appendData:data];
NSInteger rspDataLen = [responseData length];
while(rspDataLen >= 10) {
currMsg = [[NSData alloc] initWithBytesNoCopy:(void *)[responseData bytes] length:currMsgSize];
[self processMsg:currMsg];
[currMsg release];
[responseData getBytes:tmpBuffer range:NSMakeRange(currMsgSize, rspDataLen - currMsgSize)];
[responseData release];
responseData = [[NSMutableData alloc] initWithBytesNoCopy:(void *)tmpBuffer length:rspDataLen - currMsgSize];
rspDataLen = rspDataLen - currMsgSize;
}
}
Where do you allocate the first responseData?
What is [self processMsg:currMsg] doing with the data? If it is expecting the data to be around after -processMsg: returns, and it isn't explicitly making a copy, then you are in trouble.
Infact, unless you have finished with the received data before didReceiveData: returns, you need to make a copy of it somewhere, which isn't visible in the code shown.
You need to allocate the storage for tempBuffer, not pass in an uninitialised pointer;
You should look probably for a pre-rolled implementation of a simple ring buffer. There are plenty around.