NSURLConnection lost data - objective-c

I need to communicate with server with SOAP request and get response. I have quite good method which creates XML request with parameters, transform it to NSMutableURLRequest and send with NSURLConnection. All of it works fine so I'll skip this part of code. My server is kind of Magento shop so I receive different amount of data depends what request I use. When I get short responses like session ID, everything works perfect, but when response is longer (list of countries for example) my data is lost somehow. I checked it by comparison of data length in didReceiveResponse and didReceiveData
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.expectedLength = response.expectedContentLength;
self.downloadedLength = 0;
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Data recived");
self.downloadedLength = self.downloadedLength + [data length];
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.downloadedLength == self.expectedLength) {
NSLog(#"correctly downloaded");
}
else{
NSLog(#"sizes don't match");
}
str = [[NSString alloc] initWithBytes:[receivedData bytes] length:[receivedData length] encoding:NSISOLatin1StringEncoding];
NSData *tmp_Data = [[NSData alloc] initWithData:[str dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]];
parser = [[SYXmlParser alloc]initWithData:tmp_Data];
[parser startParser];
if([parser theDataArray] != nil && [[parser theDataArray]count] != 0)
{
resultArray = [[NSMutableArray alloc]initWithArray:[parser theDataArray]];
[self performSelectorOnMainThread:#selector(loadFinished) withObject:nil waitUntilDone:YES];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
When response is long I got cut XML and my comparison returned "sizes don't match". XML is not cut definitely by half, but the data is missing in the middle of text, like some of characters is missing only. The issue repeats every time in same places in same way so its not random. I tried use AFNetworking to solve this problem, but I think I cant use it properly with this SOAP request. I will be grateful for every proposition which would fix this problem.
EDIT:
I used this Charles but there is the same problem as in xcode. When I open response tab and SOAP tab the response is null, on XML tab there is an error that Charles could't parse this received data, but in overview tab there is size of response 4666 bytes. Is that mean that server gives bad response? I cant believe it because its commercial server Magento which is used with many others languages and it works.

Related

How can I check if my OS X application can connect to a specific server-- assuming there is internet connection?

I currently am using AFNetworking to determine if my application has network reachability.
NSNumber *s = notification.userInfo[AFNetworkingReachabilityNotificationStatusItem];
AFNetworkReachabilityStatus status = [s integerValue];
if (status == AFNetworkReachabilityStatusReachableViaWWAN || status == AFNetworkReachabilityStatusReachableViaWiFi) {
But, now I also need to know if my application can reach a specific server. More specifically, the server I am connecting to may be down and I need a way to determine if this is the case, from the client side, so I can notify my users appropriately.
It's a very tough google because all searches I do just point me to "How to determine network reachability". Has anybody dealt with this before, and have a solution in mind?
EDIT: #AvT recommended a promising looking solution, so I tried it like this:
self.testTSCReachabilityManager = [AFNetworkReachabilityManager managerForDomain:#"www.asdasfjsldfkjslefjslkjslfs.com"];
__weak MyObject *weakSelf = self;
[self.testReachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (weakSelf.testReachabilityManager.reachable) {
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
}];
But unfortunately, it is logging out "YES" for me, even after I have confirmed it is most definitely not reachable.
Instantiate AFNetworkReachabilityManager with class method
+ (instancetype)managerForDomain:(NSString *)domain;
and pass string with the required domain. AFNetworkReachabilityManager will check reachability of this domain.
If serverURL is an url of your server you should use it the following way:
[AFNetworkReachabilityManager managerForDomain:serverURL.host]
Update
Following code works as expected:
static AFNetworkReachabilityManager *testTSCReachabilityManager;
testTSCReachabilityManager = [AFNetworkReachabilityManager managerForDomain:#"www.asdasfjsldfkjslefjslkjslfs.com"];
[testTSCReachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (testTSCReachabilityManager.reachable) {
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
}];
[testTSCReachabilityManager startMonitoring];
Update: I actually ended up going w/ a different implementation than what Avt recommended, and did what matt recommended in the comments instead
I created an NSURLRequest and make a request to my server, then used the delegate callbacks to determine if the server was reachable. Works like a charm
-(void)checkConnectionToServers
{
NSMutableURLRequest* request = [NSMutableURLRequest new];
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"www.myserver.com"] cachePolicy:0 timeoutInterval:(NSTimeInterval)5.0];
[request setHTTPMethod:#"GET"];
[NSURLConnection connectionWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"SUCCESS");
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"FAIL");
}

How to wait till asynchronous request returns data before returning value

I have send asynchronous request to a website using the following code:
NSMutableURLRequest *requestSiteToSendData = [NSMutableURLRequest requestWithURL:[[NSURL alloc]initWithString:#"www.example.com"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
NSURLConnection *connectionToSiteToSendData = [[NSURLConnection alloc]initWithRequest:requestSiteToSendData delegate:self];
Then I used the following method defined inside NSURLConnectionDelegate to get and parse the data after the data fetching is completed.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//parse 'data'
NSString *parsedData = [self parseDataWithData:data];
}
And then in the method in which I send the asynchronous request, I return parsedData. But the returning should only happen after the data fetching is completed and hence parsing is done. I know the question arises if that is what I need then why I am not using synchronous request. It is because I don't want my other methods to hang up when the loading is going on in background.
Quick answer : if it's asynchronous, you don't want to wait the asynchronous method.
One of the bests option would be :
The object calling wanting the data should set itself as the object that runs the asynchronous method, and in didReceiveData, you call a method such as updateData:(NSString *)parsedData, which handles the newly received data
The object calling the method should use KVO to observe any change on a property of the object that runs the asynchronous method.
Tell me if you need more informations.
Asynchronous requests run on separate thread, So we don't need to worry about handling view lockup.
If you want send a synchronous request then you have to use GCD to achieve the same. And various other details like, how much data is send/received etc. will not be available in synchronous request.
Synchronous request are helpful if your code next state is dependent on data received in response of the request.
As far as i understand you want that to return data after web call is complete. so i would suggest that create any method for webcall that returns NSData and do something like this:
NSHTTPURLResponse* urlResponse = nil;
NSError *error = [[NSError alloc] init];
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&urlResponse error:&error];
if ([urlResponse statusCode] >= 200 && [urlResponse statusCode] < 300) {
// return responseData from here.
}
else {
NSLog(#"%d",[urlResponse statusCode]);
NSString *result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"%#",result);
}
and you don't want to hung up your View. so call this method in background thread. like this:
[self performSelectorInBackground:#selector(WebCallMethod) withObject:nil];
Hope it Helps!!
You have this delegate method which will execute when all the downloading is completed from tha server successfully.Use this method to do the remaining process
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
}
Ok this method gets executed when the all data is recieved.What you should do is to collect all data from -didReceiveData delegate method and then use it to parse in this method.
A must read document for you

Objective-C: Unable to send/receive data after two matchmaking two devices using Game Center

I was hoping someone would be able to help with an Objective-C problem I have relating to sending and receiving information between two devices using GameCenter once they have been through matchmaking.
I am using a textbook called Beginning iOS Game Center and Game Kit as my guide, and it is going through an example program, but I am stuck on the part where I would like to receive data from a device playing the game.
I can successfully matchmake two devices and the appropriate view to appear. I have two functions in my GameCenterManager.m file to send information - one being the following:
- (void)sendStringToAllPeers:(NSString *)dataString reliable:(BOOL)reliable
{
NSLog(#"Send String To All Peers");
NSLog(#"Data String: %#", dataString);
NSLog(#"match or session %#", self.matchOrSession);
if (self.matchOrSession == nil)
{
NSLog(#"GC Manager matchorsession ivar was not set - this needs to be set with the GKMatch or GKSession before sending or receiving data");
return;
}
NSData *dataToSend = [dataString dataUsingEncoding:NSUTF8StringEncoding];
GKSendDataMode mode;
if (reliable)
{
mode = GKSendDataReliable;
}
else{
mode = GKSendDataUnreliable;
}
NSError *error = nil;
if ([self.matchOrSession isKindOfClass:[GKSession class]])
{
NSLog(#"Match or session 1");
NSLog(#"Data to send: %#", dataToSend);
[self.matchOrSession sendDataToAllPeers:dataToSend withDataMode:mode error:&error];
}
else if ([self.matchOrSession isKindOfClass:[GKMatch class]])
{
NSLog(#"Match or session 2");
NSLog(#"Data to send: %#", dataToSend);
[self.matchOrSession sendDataToAllPlayers:dataToSend withDataMode:mode error:&error];
}
else
{
NSLog(#"GC Manager matchOrSession was not a GKMatch or a GK Session, we are unable to send data");
}
if (error != nil)
{
NSLog(#"An error occurred while sending data %#", [error localizedDescription]);
}
}
this function I call from a function in my racetohundredViewController.m file:
- (void)generateAndSendHostNumber;
{
NSLog(#"Generate and send host number");
randomHostNumber = arc4random();
NSString *randomNumberString = [NSString stringWithFormat: #"$Host:%f", randomHostNumber];
NSLog(#"the random number string is: %#", randomNumberString);
[self.gcManager sendStringToAllPeers:randomNumberString reliable: YES];
}
I successfully get the following NSLog output resulting from this:
2013-01-02 22:27:43.519 First to 50[1376:907] Send String To All Peers
2013-01-02 22:27:43.520 First to 50[1376:907] Data String: $Host:2087825492.000000
2013-01-02 22:27:43.521 First to 50[1376:907] match or session <GKMatch 0x200853d0 expected count: 0 seqnum: 2
G:1656671636:connected
reinvitedPlayers:(
)>
2013-01-02 22:27:43.522 First to 50[1376:907] Match or session 2
2013-01-02 22:27:43.523 First to 50[1376:907] Data to send: <24486f73 743a3230 38373832 35343932 2e303030 303030>
So I can see the 'Data to send' output which is great.
However I now have the command
[self.matchOrSession sendDataToAllPeers:dataToSend withDataMode:mode error:&error];
which doesn't seem to take me anywhere at all. I have the following function in GameCenterManager.m:
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
{
NSLog(#"*****Receive Data In Session");
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *dataDictionary = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:dataString, peer, session, nil] forKeys:[NSArray arrayWithObjects: #"data", #"peer", #"session", nil]];
[self callDelegateOnMainThread: #selector(receivedData:) withArg: dataDictionary error: nil];
}
But I am not seeing the NSLog output from this. Likewise, I have a function in my racetohundredViewController.m file
- (void)receivedData:(NSDictionary *)dataDictionary
{
NSLog(#"------Received Data");
}
which also doesn't get called; presumably because the previous function isn't able to call it.
I have been trying to work out why this doesn't work for a while now without any avail. Can anyone point out where I am going wrong? I hope I have put in all the relevant code but if you have any questions please ask.
Thanks to all, in advance.
I realised what I had done after looking online at other people's problems with the same thing. I had not set my delegate after finding a match, a very rookie mistake!

Street address verification

My user enters a recipients address (Street address not email). I need to verify it with the USPS so I know that it is actually an address.
I am digging through their API right now and I think I understand it but I'm not exactly sure how to go about it with objective-c.
So pretty much it works like so:
I have to create an XML request that contains the recipient name, address, and zip code.
I have to post that to their server
They respond with an XML response
Here is an example of what one of their constructed XML request looks like:
http://SERVERNAME/ShippingAPITest.dll?API=Verify&XML=<AddressValidateRequest% 20USERID="xxxxxxx"><Address ID="0"><Address1></Address1>
<Address2>6406 Ivy Lane</Address2><City>Greenbelt</City><State>MD</State> <Zip5></Zip5><Zip4></Zip4></Address></AddressValidateRequest>
A bit garbled but broken down:
http://SERVERNAME/ShippingAPITest.dll?API=Verify&XML=
<AddressValidateRequest% 20USERID="xxxxxxx">
<Address ID="0">
<Address1></Address1>
<Address2>6406 Ivy Lane</Address2>
<City>Greenbelt</City>
<State>MD</State>
<Zip5></Zip5>
<Zip4></Zip4>
</Address>
</AddressValidateRequest>
My first idea seems obvious but there maybe a better way to go about it. Since the XML feed short, should I go about construction by simple doing something along the lines of:
NSString *request = [NSString stringWithFormat:#"......"]
Where it is filled in and formatted along the lines posted above.
The second question is how to go about correctly sending this to the server?
I simply create a NSURL request and with the URL as the constructed XML string?
Here what I have but I keep getting that the URL was constructed wrong:
- (void)verifyAddress:(Recipient*)_recipient {
NSURL *_url = [NSURL URLWithString:#"http://testing.shippingapis.com/ShippingAPITest.dll?API=Verify&XML=<AddressValidateRequest%20USERID=\"********\"><Address ID=\"0\"><Address1></Address1><Address2>6406 Ivy Lane</Address2><City>Greenbelt</City><State>MD</State><Zip5></Zip5><Zip4></Zip4></Address></AddressValidateRequest>"];
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:_url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
NSString* newStr = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
NSLog(#"the response '%#'", newStr);
} else {
// Inform the user that the connection failed.
NSLog(#"error");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSString* newStr = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
NSLog(#"the response '%#'", newStr);
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
}
I get the following error:
Connection failed! Error - bad URL (null)
My only question now is, am I doing everything ok as far as NSURLConnection goes? I can play around with the URL, I just want to make sure my implementation is ok so Im not running around in circles. :P
You have % 20 in your URL. It should be %20 (no space).
There may be other problems, but that was one was easy to spot. If you are getting an error message, you need to edit your question and paste in the exact error message.
Also, you might consider using Apple's NSURLRequest and NSURLConnection classes, because more people are likely to be familiar with them so it may be easier for you to find help.
Cory, I work in the address validation industry (for SmartyStreets, where what you're trying to do is our specialty in fact) and have seen a lot of similar issues to yours.
We actually used to support an XML-endpoint for our address verification API (LiveAddress). Last year we deprecated it and deployed a new JSON format because the XML was clunky to use and had a lot of problems when it's actually just a simple task (for you, the developer).
So a few things to keep in mind... and while Rob's answer is programmatically comprehensive, these are important to consider also:
The USPS is the official source of addresses for the USA, but its core domain is not providing API service. Especially with recent financial troubles, I suspect that support and maintenance of the API will wane over time.
The License Agreement for the API you're using is quite restrictive. For example:
User agrees to use the USPS Web site, APIs and USPS data to facilitate USPS shipping transactions only. [27 Jan 2012]
Meaning, if you're shipping mail or packages via the USPS by using their API, it's fine, but for any other purpose it's not allowed and it violates the TOS.
I see you're developing for iOS. There's a great JSON library for that called TouchJSON that, in my opinion, is easier to use than XML formats in Objective-C.
While the USPS service does work, they CASS-certify private entities to provide their data at a better value (more specialty, experience, features, etc).
These and other maladies can be remedied by service from a third-party vendor. More details and reasons are documented here. Which provider you choose is up to you, but I'll be happy to personally answer any other address-validation-related questions.

How to find the 404 error cause, when I am using a webview based application and the server fails in iphone sdk

I am implementing a webview based application, in that I need to find out a way when the 404 error occurred.
Anyone's help will be much appreciated.
Thanks to all,
Monish.
In the webViewDidFinishLoad method, you can also check it this way:
NSCachedURLResponse *resp = [[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request];
NSLog(#"status code: %ld", (long)[(NSHTTPURLResponse*)resp.response statusCode]);
webViewDidFinishLoad() method writes following code and checks status code...
- (void)webViewDidFinishLoad:(UIWebView *)webview {
NSCachedURLResponse *urlResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:webview.request];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) urlResponse.response;
NSInteger statusCode = httpResponse.statusCode;
}
Here you just need to check the status of the request when it finishes or fails in webview delegate method.`
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
int status = [[[webView request] valueForHTTPHeaderField:#"Status"] intValue];
if (status == 404) {
}
}
If this doesn't help you out. Check this one.
Create an NSURLRequest with the URL you want to load. Then make the connection using NSURLConnection.
NSURLConnection has a delegate method
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
which will give the the response from the server. Please note that if you are making the connection over HTTP, the response will actually be of class NSHTTPURLResponse. The NSHTTPURLResponse can be used to get the status using the following instance method
- (NSInteger)statusCode
Then check if status = 404 or not and if yes then show your alert view. In this way you will be able to show the html page and the alert view both.
Try this, I think that if URL is wrong then the return html is nil. you can handle it there only
NSString *htmlCode = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:loadUrl]];
if (htmlCode==nil)
{
// you can handle here with an alert or any message in webview to load.
}
else
{
[myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:loadUrl]]];
}
[htmlCode release];
You can handle using htmlCode in other method like
- (void)webViewDidFinishLoad:(UIWebView *)webView
if You want to handle it after request.