I have a NSURLConnection that receives data output from a url pointing to a php script on my server.
Most of the time everything works fine and the data is retrieved in its complete form.
However, sometimes I receive NULL or broken (i.e. the bottom half) of data at:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
When this happens, if I reload the connection it will always return the same null or broken block
of data for the request.
EDIT*:
I've realized that when I receive what I thought was nil data, I actually received data
but the NSString created from this data is nil. I still don't understand why though. My php encoding output is always UTF-8 so I don't think it is an issue of encoding and besides it works most of the time with this.
I have checked the php script with that same request to verify that it is not a problem on the server side or with the php script and confirmed that it is NOT.
My code is Below:
-(void)setUpConnectionAndMakeRequest {
NSString *URLpath = #"http://www.example.com/myphp.php";
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
NSMutableURLRequest *myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[myURLRequest setHTTPMethod:#"POST"];
//I added this because I thought it may be a problem relating to cache but it isn't
NSURLCache *cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
NSString *httpBodystr = [NSString stringWithString:#"command=runscript"];
[myURLRequest setHTTPBody:[httpBodystr dataUsingEncoding:NSUTF8StringEncoding]];
[mailData setData:nil]; //mailData is a NSMutableData object which accumulates the data retrieved by the request
[NSURLConnection connectionWithRequest:myURLRequest delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSString *untrimmedDataSTR = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; //Created so I can see the data (always text) in NSLog
NSLog(#"Live Data: %#", untrimmedDataSTR); //This is where I see that the data is broken or null when it shouldn't be
[mailData appendData:data]; //Append accumulated data to NSMutableData object used later in my app.
[untrimmedDataSTR release];
}
Any help would be much appreciated.
According to the NSString reference, -initWithData:encoding: returns nil if "the initialization fails for some reason (for example if data does not represent valid data for encoding)."
That almost certainly means that the response from the server is not, in fact, UTF-8 encoded data.
The way to check would be to NSLog the data before trying to convert to an NSString:
NSLog(#"Raw Data: %#", data);
(The -description method on NSData will return a hexadecimal representation of the contents; that's what will get logged).
Related
I'm trying to fetch an XML-document from a server, but the response I'm getting in clear text is just ÿþ<.
I found out, by reading the HEX-data of the NSData that what follows after those first three bytes is the whole response I wanted, but with a nullbyte in between every byte. Now, I could just strip the data from those bytes, but if I get the same data with Netcat, there are no nullbytes there, so I figured there is something wrong with my code.
I've sent the exact same headers that the iPad-simulator has sent, but everything seems normal from there. This is the code I'm using to fetch the XML-data:
NSString *postData = #"SomePostData";
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSString *URLString = [NSString stringWithFormat:#"https://www.example.com/xmlexport?sessionid=%#", sessionID];
[request setURL:[NSURL URLWithString:URLString]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:[postData dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
And these are the delegate methods I'm using.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
myData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[myData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *myString = [[NSString alloc] initWithData:myData encoding:NSWindowsCP1252StringEncoding];
NSLog(#"%#", myString);
}
This is the response I'm getting from the server:
{ URL: https://www5.whentowork.com/cgi-bin/w2wE.dll/mgrexportemplist?SID=2835115254228 } { status code: 200, headers {
"Access-Control-Allow-Origin" = "*";
"Content-Disposition" = "attachment;filename=empdata.xml";
"Content-Encoding" = gzip;
"Content-Type" = "application/x-msdownload";
Date = "Mon, 14 Apr 2014 19:37:54 GMT";
Server = "Microsoft-IIS/6.0";
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
} }
This is the first part of the XML:
<?xml version="1.0" encoding="windows-1252" standalone="yes"?>
<!-- Generated by Exmaple.com -->
<!DOCTYPE tutorial [
<!ENTITY auml "ä">
<!ENTITY ouml "ö">
<!ENTITY uuml "ü">
<!ENTITY Auml "Ä">
<!ENTITY Ouml "Ö">
<!ENTITY Uuml "Ü">
I'm running the latest Xcode and running the code in the iOS simulator (Version 7.1 (463.9.41)) for iPad.
I really don't get this. I spent so much time trying to figure out why I only got three bytes, then later figured out I got everything, but with nullbytes in between everywhere. I do not have control over the server, so I can not change it's configuration.
EDIT: An NSLog of the data return this: (just the first couple of lines)
fffe3c00 3f007800 6d006c00 20007600 65007200 73006900 6f006e00 3d002200 31002e00 30002200
20006500 6e006300 6f006400 69006e00 67003d00 22007700 69006e00 64006f00 77007300 2d003100
32003500 32002200 20007300 74006100 6e006400 61006c00 6f006e00 65003d00 22007900 65007300
22003f00 3e000d00 0a003c00 21002d00 2d002000 47006500 6e006500 72006100 74006500 64002000
62007900 20005700 68006500 6e005400 6f005700 6f007200 6b002d00 2d003e00 0d000a00 3c002100
44004f00 43005400 59005000 45002000 74007500 74006f00 72006900 61006c00 20005b00 0d000a00
20002000 20002000 3c002100 45004e00 54004900 54005900 20006100 75006d00 6c002000 22002600
61006d00 70003b00 61007500 6d006c00 3b002200 3e000d00 0a002000 20002000 20003c00 21004500
4e005400 49005400 59002000 6f007500 6d006c00 20002200 26006100 6d007000 3b006f00 75006d00
6c003b00 22003e00 0d000a00 20002000 20002000 3c002100 45004e00 54004900 54005900 20007500
75006d00 6c002000 22002600 61006d00 70003b00 75007500 6d006c00 3b002200 3e000d00 0a002000
20002000 20003c00 21004500 4e005400 49005400 59002000 41007500 6d006c00 20002200
The fffe at the start is a byte order mark that indicates that the response is is little endian UTF-16. (See this byte order mark table.) This is further confirmed if you look at the rest of your data, where 3c00 is the little endian UTF-16 representation of <, 3f00 is ?, 7800 is x, etc.
Thus:
fffe3c00 3f007800 6d006c00 20007600 65007200 73006900 6f006e00 3d002200
31002e00 30002200 20006500 6e006300 6f006400 69006e00 67003d00 22007700
69006e00 64006f00 77007300 2d003100 32003500 32002200 20007300 74006100
6e006400 61006c00 6f006e00 65003d00 22007900 65007300 22003f00 3e00
is
<?xml version="1.0" encoding="windows-1252" standalone="yes"?>
You should therefore convert it to a string using:
NSString *myString = [[NSString alloc] initWithData:myData encoding:NSUTF16LittleEndianStringEncoding];
This will automatically handle the byte order mark, as well (eliminating that curious ÿþ at the start).
By the way, if you're ever unclear about the representation, you can always try saving the NSData to a file and then using stringWithContentsOfFile:usedEncoding:error:. Personally, I'd always first look for a the byte order mark or for obvious UTF-16 or UTF-32 data (which is generally pretty easy to identify in western languages), as we did above, but this can be useful sometimes:
NSStringEncoding encoding;
NSError *error;
NSString *string = [NSString stringWithContentsOfFile:path usedEncoding:&encoding error:&error];
if (string) {
NSLog(#"string = %#", string);
NSLog(#"encoding = %d", encoding);
} else {
NSLog(#"stringWithContentsOfFile error: %#", error);
}
It doesn't always work, but sometimes it provides interesting clues.
I am trying to fetch data from an api and I have this code:
// create the URL we'd like to query
NSString *urlString = [NSString stringWithFormat:#"https://www.googleapis.com/adsense/v1.1/reports"];
NSString *token = auth.accessToken;
NSMutableURLRequest *myURL = [NSURL URLWithString:urlString];
[myURL addValue:token forHTTPHeaderField:#"Authentication"];
// we'll receive raw data so we'll create an NSData Object with it
######
How would I complete this step? So far I have
NSData *myData =
######
// now we'll parse our data using NSJSONSerialization
id myJSON = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:nil];
// typecast an array and list its contents
NSArray *jsonArray = (NSArray *)myJSON;
NSLog(#"%#", jsonArray);
How would I connect on the 9th line?
It looks like you are putting all this code in one place (making a synchronous network request). This is typically a bad idea. You should put the first part (creating and starting the request) in one place, and put the parsing code in a separate method / block that gets called once the request is completed. (This is called an asynchronous network request.)
You should take a look at +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]. You can pass your NSURLRequest in here, and then specify the completion (parsing, etc.) in the completion handler. It will give you the NSData object you're looking for - see documentation here.
If you have more than a handful of network connections, this can get unwieldy. You may want to look at a third party library like AFNetworking, which can manage much of the tedious stuff for you, and let you focus on calling a URL, and parsing the response.
You need:
NSData *myData = [NSURLConnection sendSynchronousRequest:myURL returningResponse:nil error:nil];
This is a synchronous call. I'll suggest always using asynchronous webcalls is a good way.
Description:
I need to send a base 64 encoded string in a HTTP POST parameter that represents a file to be uploaded.
Current method: I'm using a ASIFormDataRequest and encoding my file into a base64 string like so:
NSData *data = [[NSFileManager defaultManager] contentsAtPath:#"my path here"];
NSString *base64Data = [data encodeBase64ForData];
However when uploading large files the App runs out of memory and dies a horrid death!
Proposed solution: Does anybody now how I would go about, say, reading the file from disk, converting it to base64 strings in chunks and attaching the converted base64 string to a parameter in the HTTP request?
Effectively any way that avoids reading the whole file into memory.
Any input would be greatly appreciated!
My http request code in full:
NSURL *url = [NSURL URLWithString:requestProgressURLString];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:evidence.uri];
NSString *base64Data = [data encodeBase64ForData];
NSURL *fileURL = [[NSURL alloc] initWithString:evidence.uri];
request = [ASIFormDataRequest requestWithURL:url];
request.shouldAttemptPersistentConnection = false;
[request setShowAccurateProgress:YES];
[request setPostValue:accessToken forKey:#"AccessToken"];
[request setPostValue:[[fileURL path] lastPathComponent] forKey:#"Filename"];
[request setPostValue:evidence.name forKey:#"Title"];
[request setPostValue:base64Data forKey:#"FileData"]; //wants string value (base64 encoded)
[request addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded"];
[request setTimeOutSeconds:60];
[request setUploadProgressDelegate:self];
[request setDownloadProgressDelegate:self];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
I would split this problem into two:
1. Encode large file to Base64 in chunks
2. Upload that file
1. Encode large file to Base64 in chunks:
Find some code that encodes NSData instances to Base64. Then use one NSInputStream to read data in chunks, encode it and use NSOutputStream to write to a temporary file. I found this question/answer: Is it possible to base64-encode a file in chunks? Make the chunk size a multiple of 3 and it should work. You may want to move this to background thread.
Important: This will create a temporary file, that is larger than the original. There must be enough space on device for that moment. After you begin uploading you can delete it.
2. Upload that file
It seems that you have this already done. You can use that ASIFormDataRequest exactly like in your example. And since it makes a private copy of the file (it builds the multipart POST file), you can delete your copy.
If you want to optimize it by parallelizing these steps, you will need to find another way to upload the multipart file.
Instead of reading the file in one hit you need to read it in chunks
You can use an input stream to do this
Apple have some info on here
Basically you need to create an NSInputStream pointed at your file as in the example
- (void)setUpStreamForFile:(NSString *)path {
// iStream is NSInputStream instance variable
iStream = [[NSInputStream alloc] initWithFileAtPath:path];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
}
You can then read the data in chunks using the NSStreamDelegate method
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
Again Apple give an example of this
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(!_data) {
_data = [[NSMutableData data] retain];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
[bytesRead setIntValue:[bytesRead intValue]+len];
// DO SOMETHING with DATA HERE
} else {
NSLog(#"no buffer!");
}
break;
}
The size of chunks you choose will need to be a balance of memory performance vs network performance as there is obviously some overhead with the HTTP headers
You can also further optimise by sending your NSData directly ( and therefore save the base64 overhead) check this post
File Upload to HTTP server in iphone programming
I have data on a server and I am trying getting this data as a piped string. I wrote the sever-side code but I don't know how can I retrieve this data and parse it in iOS. Here is an example of my data. Is there any article or code that can help me?
A few starting points: For retrieving a stream of data from a server, have a look at Apple's Stream Programming Guide. To parse your data, you can use a NSScanner (String Programming Guide: Scanners) or a CFXMLParser (XML Programming Topics for Core Foundation) if you have valid XML data.
Your output doesn't seem to be in a valid format like xml or json. But if your glat with the content as a string inside your app use this code snippet
- (void)viewDidLoad{
[super viewDidLoad];
NSURLRequest *site_request =
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://...."]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *site_connection =
[[NSURLConnection alloc] initWithRequest:site_request
delegate:self];
}
- (void)connection:(NSURLConnection *)site_connection didReceiveData:(NSData *)data{
NSString *site_response =
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(#"OUTPUT: %#", site_response);
}
The log will put out the string inside the console.
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.