iOS HTTP request - getting the error response message - ios

This is my bit of code doing a GET request to a REST api.
Im not sure how to get back the message if I get an error:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL *URL = [NSURL URLWithString:urlString];
[request setURL:URL];
[request setHTTPMethod:#"GET"];
NSError *err = nil;
NSHTTPURLResponse *res = nil;
NSData *retData = [NSURLConnection sendSynchronousRequest:request returningResponse:&res error:&err];
if (err) // This part is never called.
{
NSLog(#"Error: %#", err);
}
else
{
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", res); // This part is called
}
else
{
}
}
I want to get the error message if it was not successful. But the if (err) block is never called. err is still null, although the statuscode is 400.
And if successful I will get back a json response.
In the code above I get back a statusCode of 400

The error block is not called because the error object is created only if a system level error occurs. This does not happen because the request is sent correctly and the server sends a response. If you are in control of the server, you should probably make it return status code 200 and include an app level status code in the response, that would tell your app that the entered credentials are incorrect.
Edit:
To get status message you can use
+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode
This is a class method of the NSHTTPURLResponse class.
if (res.statusCode != 200)
{
// show the user the status message
NSLog(#"Error: %#", [NSHTTPURLResponse localizedStringForStatusCode: res.statusCode]); // This part is called
}

Take a look at the NSError class reference:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/Reference/Reference.html
You can try to log the error message from the localizedDescription.

you are receiving this status code because- The Web server (running the Web site) thinks that the data stream sent by the client (e.g. your Web browser or our CheckUpDown robot) was 'malformed' i.e. did not respect the HTTP protocol completely. So the Web server was unable to understand the request and process it
to log above problem in respect to ios visit this link

If you read the documentation of sendSynchronousRequest...
error
Out parameter used if an error occurs while processing the request. May be NULL.
this mean that erro will be a valid NSError object in case there is a problem to resolve the request, like a malformed URL.
If the request can be resolved error will be NULL and according with HTTP protocol and depending to the server that you are trying to connect, the NSHTTPURLResponse object will contain all the information about the request.
In general is an error think that every status code different than 200 is an error, for example for a REST based API 204 mean empty data, and in this case the request is finished successfully but the requested resource is just empty data, and this is not an error.
So about your question, is absolutely fine that error is NULL most of the time, if is not mean that there is an issue before reach the target server, in general you have to consider both, error and according to the server that you are trying to talk the status code maps, in most of cases the REST pattern

Related

SoundCloud API iOS Fails to upload audio with a HTTP 422 error

I'm trying to put a super basic Soundcloud feature in my app to upload single .wav files using their UI. I followed their guide and I didn't really need anything outside of the bare bones share menu so I assumed this code would work:
NSURL *trackURL = [[NSURL alloc] initWithString:[docsDir stringByAppendingPathComponent:fileToShare]];
SCShareViewController *shareViewController;
shareViewController = [SCShareViewController shareViewControllerWithFileURL:trackURL
completionHandler:^(NSDictionary *trackInfo, NSError *error){
if (SC_CANCELED(error)) {
NSLog(#"Canceled!");
} else if (error) {
NSLog(#"Ooops, something went wrong: %#", [error localizedDescription
]);
} else {
// If you want to do something with the uploaded
// track this is the right place for that.
NSLog(#"Uploaded track: %#", trackInfo);
}
}];
// If your app is a registered foursquare app, you can set the client id and secret.
// The user will then see a place picker where a location can be selected.
// If you don't set them, the user sees a plain plain text filed for the place.
[shareViewController setFoursquareClientID:#"<foursquare client id>"
clientSecret:#"<foursquare client secret>"];
// We can preset the title ...
[shareViewController setTitle:#"Funny sounds"];
// ... and other options like the private flag.
[shareViewController setPrivate:NO];
// Now present the share view controller.
[self presentModalViewController:shareViewController animated:YES];
[trackURL release];
However I get an HTTP 422 error with this appearing in my debug console:
2016-02-29 11:04:47.129 synthQ[801:443840] parameters: {
"track[asset_data]" = "/var/mobile/Containers/Data/Application/EE58E5CA-B30C-44EB-B207-EB3368263319/Documents/bb.wav";
"track[downloadable]" = 1;
"track[post_to][]" = "";
"track[sharing]" = public;
"track[tag_list]" = "\"soundcloud:source=synthQ\"";
"track[title]" = "Funny sounds";
"track[track_type]" = recording;
}
2016-02-29 11:04:47.164 synthQ[801:444011] -[NXOAuth2PostBodyStream open] Stream has been reopened after close
2016-02-29 11:04:47.373 synthQ[801:443840] Upload failed with error: HTTP Error: 422 Error Domain=NXOAuth2HTTPErrorDomain Code=422 "HTTP Error: 422" UserInfo={NSLocalizedDescription=HTTP Error: 422}
Does anyone have any ideas what could be going wrong here?
Thanks!
One of the reason to get HTTP 422 error in SoundCloud is:
If you are trying to upload a file for a first time with a new account, you need to verify your email address to complete your SoundCloud registration in order to upload your files.
There might be other reasons for this error, however for my case it was the case and that solved the problem.
You cannot reference external resources while uploading tracks. You
therefor need to download the track to your computer and then perform
the actual upload to SoundCloud.
Source
Soundcloud file uploading issue HTTP 442
I managed to solve this by using a different constructor that passes the audio file as an NSData object:
shareViewController = [SCShareViewController shareViewControllerWithFileData:[NSData dataWithContentsOfFile:[docsDir stringByAppendingPathComponent:fileToShare]]
completionHandler:^(NSDictionary *trackInfo, NSError *error){
if (SC_CANCELED(error)) {
NSLog(#"Canceled!");
} else if (error) {
NSLog(#"Ooops, something went wrong: %#", [error localizedDescription
]);
} else {
// If you want to do something with the uploaded
// track this is the right place for that.
NSLog(#"Uploaded track: %#", trackInfo);
}
}];

How to do Access Token Request in Fitbit API Integration - iOS?

I did OAuth2 by using OAuth2 - https://github.com/trongdth/OAuth2-for-iOS ,
I successfully logged in and got response
{
"kOAuth_AccessToken" = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NDIzODY2NzEsInNjb3BlcyI6Indsb2Mgd3BybyB3bnV0IHdzbGUgd3NldCB3d2VpIHdociB3YWN0IHdzb2MiLCJzdWIiOiIzRFJQQzYiLCJhdWQiOiIyMjlROVEiLCJpc3MiOiJGaXRiaXQiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4iLCJpYXQiOjE0NDIzODMwNzF9.5vTYvUAuvMflnOw_7cc1nZoighhtUx4RU26-Q7SewzQ";
"kOAuth_AuthorizeURL" = "https://www.fitbit.com/oauth2/authorize";
"kOAuth_Callback" = "http://oauth-callback/fitbit";
"kOAuth_ClientId" = string;
"kOAuth_ExpiredDate" = "";
"kOAuth_RefreshToken" = 97ad2a073f40f21974c8d27cdb74523047f39e98cd2adcfd6f6cc3eb92522d53;
"kOAuth_Scope" = "activity heartrate location nutrition profile settings sleep social weight";
"kOAuth_Secret" = string;
"kOAuth_TokenURL" = "https://api.fitbit.com/oauth2/token";
}
Here - how can I get code URI parameter value in the callback URI?
According to Fitbit doc's ---
Access Token Request:-
When a user authorizes your application in the Authorization Code Grant flow, your application must exchange the authorization code for an access token. The code is only valid for 10 minutes.
Authorization Header:-
The Authorization header should be set to Basic followed by a space and a Base64 encoded string of your application's client id and secret concatenated with a colon.
I did this by making use of AFNetworking lib, Please have a look on code (header)-
-(AFHTTPSessionManager *)getSessionManager {
if (!self.sessionManager){
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = nil;
NSURL *url = [NSURL URLWithString:FITBIT_BASE_URL];
self.sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:url sessionConfiguration:sessionConfiguration];
}
// [self.sessionManager.requestSerializer setValue:Appdelegate.fitbitAuthorizationString forHTTPHeaderField:#"Authorization"];
[self.sessionManager.requestSerializer setAuthorizationHeaderFieldWithUsername:Appdelegate.fitbitOAuthClientId password:Appdelegate.fitbitOAuthClientSecret];
self.sessionManager.responseSerializer = [JSONResponseSerializerWithData serializer];
self.sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
return self.sessionManager;
}
and requested token access - code (parameter's) is
NSDictionary *tempParamsDict = #{#"client_id":[JsonUtil stringFromKey:#"kOAuth_ClientId" inDictionary:dictResponse], #"grant_type":#"authorization_code", #"redirect_uri":[JsonUtil stringFromKey:#"kOAuth_Callback" inDictionary:dictResponse], #"code":#""};
My request is
[dP postJsonContentWithUrl:#"oauth2/token" parameters:tempParamsDict completion:^(BOOL success, id responseObject, NSError *error) {
// NSLog(#"%#", responseObject);
if (success) {
NSLog(#"Response: %#", responseObject);
}
else {
NSLog(#"%#", error.localizedDescription);
/*
You got 404 response ,The 404 or Not Found error message is a HTTP standard response code indicating that the client was able to communicate with the server, but the server could not find what was requested.
Make sure that you have valid url, try to put your link in the browser and see if it is correct, if the url you requested have special headers
*/
}
}];
I am getting - Request failed: not found (404) error.
What have I missed and How can I proceed further to get token and access Fitbit APIs?
Update
That was happening due to access code so I tried to access code, now I am getting this error
description of error->_userInfo:
{
NSErrorFailingURLKey = "https://api.fitbit.com/oauth2/token.json";
NSErrorFailingURLStringKey = "https://api.fitbit.com/oauth2/token.json";
NSLocalizedDescription = cancelled;
}
Thanks a lot in advance
Instead of NSURLSession use NSURLConnection to request data from Fitbit APIs.

NSURLConnection GET request returns -1005, "the network connection was lost"

I am trying to make a simple GET request using NSURLConnection in XCode 6 (Beta7 2) on iOS 8 SDK, which is failing with "Code 1005, the network connection was lost". The call fails when I try to fetch http://www.google.com or a few other sample pages from the web, but succeeds if I make a request to a simple HTTP server on localhost (python -m SimpleHTTPServer). I have also tried using AFNetworking library (2.4.1) - URLs that fail with NSURLConnection also fail with the library.
Here's my code -
NSString * url = #"http://0.0.0.0:8000";
// NSString * url = #"http://www.google.com";
NSLog(#"URL : %#", url);
// Mutable is probably not required, but just in case it REALLY WANTS me to set HTTP method
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[theRequest setHTTPMethod:#"GET"];
NSURLResponse *urlResponse = nil;
NSError *error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&urlResponse
error:&error];
if (error == nil) {
NSString *response = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(response);
} else {
NSString *response = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#", [error userInfo]);
}
Logs:
2014-09-11 17:34:23.950 SearchExample[5092:2074687] URL : http://www.google.com
2014-09-11 17:34:24.023 SearchExample[5092:2074687] {
NSErrorFailingURLKey = "http://www.google.com";
NSErrorFailingURLStringKey = "http://www.google.com";
NSLocalizedDescription = "The network connection was lost.";
NSUnderlyingError = "Error Domain=kCFErrorDomainCFNetwork Code=-1005 \"The network connection was lost.\" UserInfo=0x7fc8515640a0 {NSErrorFailingURLStringKey=http://www.google.com/, NSErrorFailingURLKey=http://www.google.com/, _kCFStreamErrorCodeKey=57, _kCFStreamErrorDomainKey=1, NSLocalizedDescription=The network connection was lost.}";
"_kCFStreamErrorCodeKey" = 57;
"_kCFStreamErrorDomainKey" = 1;
}
2014-09-11 17:34:24.023 SearchExample[5092:2074687] URLResponse: (null)
I have seen internet connectivity failing on the iPhone 6 simulator recently, resulting in the same errors. My Mac had a working internet connection the simulator did not. Restarting the simulator fixed the issue.
I was getting this error consistently on iOS 9 with certain network calls. Two were working fine but another two were not.
It turned out to be caused by some incorrect parameters I was passing with the request's body... I wouldn't have expected that to cause a -1005 error... but it did.. Getting rid of the unnecessary parameters made it all work!
I've tried everything suggested on at least 15 answers from Google but not one of them solved my problem until I tried the following which totally addressed my issue. It appears that Wi-Fi connections can become corrupt on the Mac so if you remove the specific connection you are using and then connect again (by choosing the network and entering your password again) then this will fix the issue and no more dreaded -1005 “the network connection was lost” errors.
Go to the Wi-Fi symbol on your Mac's menubar and "Turn Wi-Fi Off"
Then choose "Open Network Preferences" (from the same menu, at the bottom).
In the bottom right-hand corner of the Network panel, choose "Advanced".
Select the network connection you were previously connected to.
Hit the minus symbol right below this table to delete this connection.
Hit "OK" for this window.
Hit "Apply" on the Network window.
Go back to the Wi-Fi symbol on your menubar and turn Wi-Fi back on.
Wait for your network connection to appear and then select it (and it will now ask for a password again because you deleted the connection info).
It will now remember this newly refreshed connection which should solve the problem!
Try to change request serialization in AFNetworking http or json. in my case that was json then i set to http. Now that is working.
[[VTNetworkingHelper sharedInstance] performRequestWithPath:#"Your url " withAuth:YES forMethod:#"POST" withRequestJSONSerialized:NO withParams:params withCompletionHandler:^(VTNetworkResponse *response) {
if (response.isSuccessful) {
}else {
}];
I have observed this issue occurs when you keep simulator active and your mac goes to sleep for long duration (say 5 to 10 hours). Then all of sudden you run app on simulator the it displays log as
NSURLConnection GET request returns Code=-1005 "The network connection was lost."
The solution is to simply quit simulator, clean project and re-run project.
This worked for me!
I had Similar issue and restarting simulator didn't work. In my case I was able to hit web service alternatively like in odd time it would be successful and in even time it threw me with this error. I know its weird but it was the case somehow. Solved it with following approach in swift :
let urlconfig = NSURLSessionConfiguration.defaultSessionConfiguration()
urlconfig.timeoutIntervalForRequest = 1
urlconfig.timeoutIntervalForResource = 1
let session = NSURLSession(configuration: urlconfig)
let task = session.dataTaskWithRequest(request){(data,response,error) in
//Processing
}
task.resume()
Simple & sample solution, tested many times, working perfect.
//Check response error using status code, and if you get -1005 then call that api again.
if let strErrorReasonCode : Int = response.response?.statusCode {
if authentication_Errors_Range.contains(Alamofire_Error) {
self.POST(urlString, paramaters: paramaters, showLoader: showLoader, success: { (responseObject) in
if response.result.isSuccess {
if let value = response.result.value {
let dictResponce = self.isValidated(value as AnyObject)
if dictResponce.0 == true {
success(dictResponce.1)
}
else {
failure(dictResponce.1)
}
}
}
}, failure: {_ in
failure(jsonResponce)
})
}
}

Why am I not getting an error when sending data through the Game Center without internet?

-(BOOL)sendMessage:(NSMutableDictionary *)_message {
//*** Process _message and convert it to NSData here ***//
NSError *error;
BOOL success = [currentMatch sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
if (error != nil) {
NSLog(#"Sending data ERROR");
}
return success;
}
I started a match (stored in currentMatch) with another player and continuously sent data using the above method. Then I turned off the wifi.
However, I am not getting the "Sending data ERROR" log message at all.
Why? I turned off the internet connection, so why is there no error here? What could possibly lead to this scenario?
I've also confirmed that the dictionary I am sending is properly encoded into an NSData object, and success is returning YES.
As per the documentation
Return Value
YES if the data was successfully queued for transmission; NO if the match was unable to queue the data.
The method only enqueues the data for transmission, which happens asynchronously.
If you want to monitor the state of the transmission, implement the proper GKMatchDelegate delegate methods, such as match:didFailWithError:.
However, as stated in the documentation:
The match queues the data and transmits it when the network becomes available.
so if you try to perform the method with no network, the transfer just won't happen until the network is back, meaning that you won't see it failing.
By the way you should check the return value, instead of the error, since the error might be nil despite the operation being unsuccessful.
NSError *error;
BOOL success = [currentMatch sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
if (!success) {
NSLog(#"Sending data ERROR\n%#", error);
}
return success;
You need to check the return value of the method to know whether or not an error occurred. You cannot test the error parameter directly.
if (!success) {
NSLog(#"Sending data ERROR -- %#", error);
}
return success;
As to why you don't get an error, that send method is asynchronous. It simply enqueues the data for transmission and immediately returns. You have to catch the error through some other means (I'm not steeped in GameKit to know what that other means might be).

Simple RestKit login without using JSON

It's been two days that I'm looking for a simple code for RestKit API for logging into a website without using JSON. Here is the code that I wrote so far:
- (void)login
{
[RKClient clientWithBaseURLString:#"http://Mywebsite.com/login.php"];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObject:#"user" forKey:#"username"];
[params setObject:#"pass" forKey:#"password"];
[params setObject:#"login" forKey:#"type"];
[[RKClient sharedClient] post:#"/login" params:params delegate:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
NSLog(#"HTTP status code: %d", response.statusCode);
NSLog(#"HTTP status message: %#", [response localizedStatusCodeString]);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSRange range = [[error localizedDescription] rangeOfString:#"-1012"];
if (range.length > 0){
//Do whatever here to handle authentication failures
}
RKLogError(#"Hit error: %#", error);
}
Here is the log that I receive:
2012-09-08 21:44:14.633 RestKitTest[889:fb03] I restkit:RKLog.m:33 RestKit initialized...
2012-09-08 21:44:15.010 RestKitTest[889:fb03] I restkit.network.reachability:RKReachabilityObserver.m:369 Network availability has been determined for reachability observer <RKReachabilityObserver: 0x6c5a560 host=0.0.0.0 isReachabilityDetermined=YES isMonitoringLocalWiFi=NO reachabilityFlags=-R -----l->
2012-09-08 21:44:21.021 RestKitTest[889:fb03] I restkit.network:RKRequest.m:676 Status Code: 404
2012-09-08 21:44:21.036 RestKitTest[889:fb03] HTTP status code: 404
2012-09-08 21:44:21.090 RestKitTest[889:fb03] HTTP status message: not found
Any idea how I can fix this issue would be appreciated.
I'd say it 404s because you set baseURL as http://Mywebsite.com/login.php yet you POST as post:#"/login". Do you really want to access http://Mywebsite.com/login.php/login?
The baseURL should be set to a common prefix to all your backend calls, in your case http://Mywebsite.com, the resource itself should be posted as post:#"/login.php". The resource name and baseURL are concatenated before the request is sent.
You'll need to provide info on what login.php is doing. I don't see why it should 404 if the script url resolves correctly.
Also, if that PHP script does a redirect on success, you should use the RKClient method post:usingBlock: and inside that block do request.followRedirect = NO;. That way you'll get the correct status code, if you are returning one.
If may also help to log more of the response:
NSLog(#"Header fields: %#", response.allHeaderFields);
NSLog(#"Body: %#", response.bodyAsString);

Resources