I have app iPhone app which will be using API's call.
I made successful call with Username/Password with NSURLSession and receiving token from server....
NSString *username = #"admin";
NSString *password = #"test";
NSString *authString = [NSString stringWithFormat:#"%#:%#",
username,
password];
// 2 - convert authString to an NSData instance
NSData *authData = [authString dataUsingEncoding:NSUTF8StringEncoding];
// 3 - build the header string with base64 encoded data
NSString *authHeader = [NSString stringWithFormat: #"Basic %#",
[authData base64EncodedStringWithOptions:0]];
// 4 - create an NSURLSessionConfiguration instance
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
// 5 - add custom headers, including the Authorization header
[sessionConfig setHTTPAdditionalHeaders:#{
#"Accept": #"application/json",
#"Authorization": authHeader
}
];
// 6 - create an NSURLSession instance
NSURLSession *session =
[NSURLSession sessionWithConfiguration:sessionConfig delegate:self
delegateQueue:nil];
// 7 - create an NSURLSessionDataTask instance
NSString *urlString = #"http://test.myserver.am/api/authentication/Login?username=admin&password=test";
NSURL *url = [NSURL URLWithString:urlString];
NSURLSessionDataTask *task = [session dataTaskWithURL:url
completionHandler:
^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
if (error)
{
// do something with the error
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200)
{
arrTokenData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
[self getDomain];
} else {
// failure: do something else on failure
NSLog(#"httpResponse code: %#", [NSString stringWithFormat:#"%ld", (unsigned long)httpResponse.statusCode]);
NSLog(#"httpResponse head: %#", httpResponse.allHeaderFields);
return;
}
}];
// 8 - resume the task
[task resume];
Now I am using token received from server and making another call to get user Data......
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSString *authValue = [arrTokenData valueForKey:#"Token"];
//Configure session with common header fields
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{#"bearer": authValue};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSString *url = #"http://test.myserver.am/api/mobile/LookUps/getuserdata";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (!error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200)
{
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:nil];
//Process the data
}
}
}];
[task resume];
but I am receiving below status code and ....request is not getting successful....
httpResponse code: 500
httpResponse head: { "Cache-Control" = "no-cache"; "Content-Length" = 36; "Content-Type" = "application/json; charset=utf-8"; Date = "Thu, 06 Oct 2016 12:16:58 GMT"; Expires = "-1"; Pragma = "no-cache"; Server = "Microsoft-IIS/8.5"; "X-AspNet-Version" = "4.0.30319"; "X-Powered-By" = "ASP.NET"; }
****Please note the same APIs is working fine from another(xamarin APP) platform....**
I am using Objective-C.... IOS10
is there my sending token request is not proper....?
please help me out ..... I am stuck here from yesterday...
As has already been mentioned, I'm pretty sure it should be:
NSString *authValue = [NSString stringWithFormat:#"Bearer %#",
[arrTokenData valueForKey:#"Token"]];
sessionConfiguration.HTTPAdditionalHeaders = #{#"Authorization": authValue};
With that said, a 500 error is an internal server error, not an authentication error. It seems likely that the real problem has nothing to do with authentication, and that your request itself is malformed in some way or that there is a bug in the server-side code that you're somehow tickling.
Also, your code doesn't seem to be checking to see if the token is actually present in the response, unless you're doing that elsewhere.
I would start by checking to make sure the token is actually there, and if it is, enable whatever debugging you can enable on the server and look through the logs to try to figure out what is causing the 500 error on the server side. Chances are, the fix will be obvious once you see what's actually happening on the server side.
Firstly, you should not pass username / password in the ULR in the first authentication call, since you already pass it as a header field. Parameters in the URL are not secure. Sensitive data should always be passed by using POST instead of GET method. But this is not the problem.
Try set the header field in the second call like this:
NSString *authorizationHeader = [NSString stringWithFormat: #"Bearer %#", authValue];
[sessionConfig setHTTPAdditionalHeaders:#{
#"Accept": #"application/json",
#"Authorization": authorizationHeader
}
];
Basically, the auth header field should look like this (from postman):
image
Attention: I did not test / build my code!
Related
We are working on an iOS project that involves sending emails through the Gmail API and we are having trouble finding documentation on how to actually do this.
First, we haven't completely figured out authentication. We are using AppAuth to handle that, and it's worked pretty well so far, but we are not quite sure how to link that up to the Gmail API in our code.
Second, how do we send the message itself? We have the content and everything formatted, we just can't figure out how to actually send the message. All we are looking to do is send a simple message to a specified email address from the user's own email account; no attachments or anything like that. We have seen a couple swift examples, however we would prefer to use Objective C. Any ideas on how we could do this?
Update:
After playing around with things a bit more, we found another way to connect to Gmail. Instead of using the classes from the Google API Objective C Client for REST, we are simply trying to send the email using an HTTP POST method. This appears to be way easier than dealing with all of the errors we were getting before. The only problem we have now is that we still can't quite send messages. With nearly everything we've tried, the API just creates an empty message and puts it in our Sent mailbox; that's it. Here's what we have right now:
- (void)sendEmail{
NSURL *userinfoEndpoint = [NSURL URLWithString:#"https://www.googleapis.com/upload/gmail/v1/users/TEST_USERNAME/messages/send?uploadType=media"];
NSString *currentAccessToken = _authState.lastTokenResponse.accessToken;
[self logMessage:#"Trying to authenticate...."];
// Handle refreshing tokens
NSString *message = [NSString stringWithFormat:#"{\"raw\": \"%#\"}",[self generateMessage]];
NSLog(#"%#", message);
// creates request to the userinfo endpoint, with access token in the Authorization header
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
NSString *authorizationHeaderValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[request addValue:authorizationHeaderValue forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"POST"];
[request setValue:#"message/rfc822" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[message length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:nil
delegateQueue:nil];
// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
// Handle response
}];
[postDataTask resume];
}];
}
- (NSString *)generateMessage{
NSString *message = [NSString stringWithFormat:#"From: <TEST_USER#domain.com>\nTo: <TEST_USER#domain.com>\nSubject: Test\n\nThis is a test"];
NSString *rawMessage = [message stringByReplacingOccurrencesOfString:#"\\n" withString:#"\n"];
NSData *encodedMessage = [rawMessage dataUsingEncoding:NSUTF8StringEncoding];
NSString *encoded = [encodedMessage base64EncodedStringWithOptions:0];
NSLog(#"%#", encoded);
return encoded;
}
We have tested the encoding part and it is making a proper base64 string, however after that point, something clearly is not formatted right or something. We get a confirmation that the message was successfully created, however all the API does is create an empty email with no recipient, subject, or body. Any ideas on what we could do to get this to work?
I'm not an expert in this but I remembered we have done something similar in the past. Follow the instructions at the following link and make sure that you select the proper option in Gmail API wizard
https://developers.google.com/gmail/api/quickstart/ios?ver=objc
I hope you find this helpful
After numerous experimentations, here is the code that seems to finally work for me, i worked it off your example above.
1st you need to create google project in dev console, get its Client ID and Api-Key(this may not be necessary) and implement Google SignIn in AppDelegete in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method:
[GIDSignIn sharedInstance].clientID = #"your proj client id here";
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes=[NSArray arrayWithObjects:#"https://www.googleapis.com/auth/gmail.send",#"https://www.googleapis.com/auth/gmail.readonly",#"https://www.googleapis.com/auth/gmail.modify", nil];
Now sending emails:
// refresh token
appDelegate.delAuthAccessToken=#"";
[[GIDSignIn sharedInstance] signInSilently];
NSDate *timeStart = [NSDate date];
NSTimeInterval timeSinceStart=0;
while([appDelegate.delAuthAccessToken isEqualToString:#""] && timeSinceStart<10){//wait for new token but no longer than 10s should be enough
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]];//1sec increment actually ~0.02s
timeSinceStart = [[NSDate date] timeIntervalSinceDate:timeStart];
}
if (timeSinceStart>=10) {//timed out
return;
}
//compose rfc2822 message AND DO NOT base64 ENCODE IT and DO NOT ADD {raw etc} TOO, put 'To:' 1st, add \r\n between the lines and double that before the actual text message
NSString *message = [NSString stringWithFormat:#"To: %#\r\nFrom: %#\r\nSubject: EzPic2Txt\r\n\r\n%#", appDelegate.delToEmails, appDelegate.delAuthUserEmail, appDelegate.delMessage];
NSURL *userinfoEndpoint = [NSURL URLWithString:#"https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=media"];
NSLog(#"%#", message);
//create request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];//message is plain UTF8 string
//add all headers into session config, maybe ok adding to request too
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{
#"api-key" : #"api-key here, may not need it though",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", appDelegate.delAuthAccessToken],
#"Content-type" : #"message/rfc822",
#"Accept" : #"application/json",
#"Content-Length": [NSString stringWithFormat:#"%lu", (unsigned long)[message length]]
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
}];
[postDataTask resume];
Hope it helps somebody
In my app I used to be able to use MailCore2 but it got blocked by Google (I got access denied when I switched to permitted send, readonly and modify scopes) since MailCore2 works only with FULL permissions. Google allowed to use ONLY send, readonly and modify scopes. There is no guide lines how to use their "great restful api" with Gmail in iOS though, so it seems like HTTP POST is the last resort until they shut it down too.
I cannot have my app to be deemed by Google as insecure. If you are OK with that you can still use MailCore2, no problem.
Receiving email with HTTP GET:
1st get up to 20 unread messages ids:
//get IDs of no more than 20 unread messages
//in query you can add extra filters, say messages only from specific emails
NSString *query=#"from:aaa#gmail.com|from:bbb#yahoo.com";
NSString *tmpStr=[NSString stringWithFormat:#"https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=20&q=\"is:unread\" \"%#\"",query];
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{#"api-key" : #"your api key here",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", yourTokenHere],
#"Accept" : #"application/json"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
if (!error){
NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
long jsonMsgsCnt = [[jsondata valueForKey:#"resultSizeEstimate"] longValue];
if(jsonMsgsCnt>0){
NSMutableArray *jsonMsgs = [jsondata objectForKey:#"messages"];
for (NSMutableDictionary *tmp in jsonMsgs){
[delMsgsReceived addObject:[tmp objectForKey:#"id"]];
}
}
NSLog(#"retrieve Email Id postDataTask n msg:%li",delMsgsReceived.count);
}else{
NSLog(#"retrieve Email Id postDataTask error:%#",error.description);
}
}];
[postDataTask resume];
Now delMsgsReceived contains messagesIds. Process them to get actual emails one by one:
NSString *tmpStr=[NSString stringWithFormat:#"https://www.googleapis.com/gmail/v1/users/me/messages/%#?format=full", msgId];//supply message id here
NSString *tmpStrURL=[tmpStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *userinfoEndpoint = [NSURL URLWithString:tmpStrURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:userinfoEndpoint];
[request setHTTPMethod:#"GET"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
configuration.HTTPAdditionalHeaders = #{
#"api-key" : #"your api key",
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", your auth token],
#"Accept" : #"application/json"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// performs HTTP request
NSURLSessionDataTask *postDataTask =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
// Handle response
if (!error){
NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSString *body=[jsondata objectForKey:#"snippet"];//not full msg!
//for full message get the whole payload and extract what you need from there NSMutableArray *jsonPayload = [[jsondata objectForKey:#"payload"] objectForKey:#"headers"];
}else{
//deal with error
NSLog(#"retrieving message error:%#",error.description);
}
}];
[postDataTask resume];
In my App I've configured a proxy for the NSURLSessionConfiguration object so the App can use the proxy. When the proxy requires authentication, the App will ask for username and password via the delegate method "URLSession:task:didReceiveChallenge:completionHandler:" and makes sure that the request can continue.
Normal in HTTP requests, but the HTTPS requests popup a dialog stating the proxy authentication is required and gives the user the choice to do this "later" or directly go to the system settings.
In addition, the dialog pops up even before the delegate method "didReceiveChallenge" of the NSUrlSession above, the application does not get an opportunity to provide credentials before iOS displays it.
Does anyone else seeing this and knows how to fix this?
proxy server response header:
HTTP/1.1 407 Proxy Authentication Required
Content-Type: text/html
X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
Proxy-Authenticate: Basic realm="Basic"
Proxy-Authenticate: Digest realm="Digest",
nonce="xxxxxxxxxxxxxxxxxxxxxxxxxxx", qop="auth", stale=false
X-Cache: MISS from proxy.xxxx.com
Via: 1.1 proxy.xxxx.com
my demo code:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
config.connectionProxyDictionary = #{
#"HTTPEnable" : #(1),
(NSString *)kCFStreamPropertyHTTPProxyHost : #"proxy.xxxx.com",
(NSString *)kCFStreamPropertyHTTPProxyPort : #(1234),
#"HTTPSEnable": #(1),
(NSString *)kCFStreamPropertyHTTPSProxyHost :#"proxy.xxxx.com",
(NSString *)kCFStreamPropertyHTTPSProxyPort : #(4321)
};
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://www.xxxxx.com"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
NSURLSessionTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(#"result:%# error: %#",data, error.description);
}];
[task resume];
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidReceiveChallenge:(NSURLAuthenticationChallenge *)challengecompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
NSLog(#"task:%#",challenge.protectionSpace.authenticationMethod);
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPDigest) {
NSURLCredential *cren = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceNone];
if (completionHandler) {
completionHandler(NSURLSessionAuthChallengeUseCredential,cren);
}
}
}
I found the solution here.It seems like such problems are bugs.Instead of using the URLSession:task:didReceiveChallenge:completionHandler: delegate, a good workaround is to add a "Proxy-Authorization" header to the NSURLSessionConfiguration.Here is the code based on here:
// 1 - define credentials as a string with format:
// "username:password"
//
NSString *username = #"USERID";
NSString *password = #"SECRET";
NSString *authString = [NSString stringWithFormat:#"%#:%#",
username,
password];
// 2 - convert authString to an NSData instance
NSData *authData = [authString dataUsingEncoding:NSUTF8StringEncoding];
// 3 - build the header string with base64 encoded data
NSString *authHeader = [NSString stringWithFormat: #"Basic %#",
[authData base64EncodedStringWithOptions:0]];
// 4 - create an NSURLSessionConfiguration instance
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
// 5 - add custom headers, including the Authorization header
[configuration setHTTPAdditionalHeaders:#{
#"Proxy-Authorization": authHeader
}
];
// 6 - set proxy dictionary
NSDictionary *proxyDict = #{
#"HTTPEnable" : #1,
(NSString *)kCFStreamPropertyHTTPProxyHost : #"ip",
(NSString *)kCFStreamPropertyHTTPProxyPort : proxyPortNumber,
#"HTTPSEnable" : #1,
(NSString *)kCFStreamPropertyHTTPSProxyHost : #"ip",
(NSString *)kCFStreamPropertyHTTPSProxyPort : proxyPortNumber,
};
configuration.connectionProxyDictionary = proxyDict;
// 7 - create an NSURLSession instance
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:nil delegateQueue:operationQueue];
I am creating an app that uses Google Place. Before I used to use Yahoo's api and had to use a url that was responsible for local search that was provided by yahoo. The url was following:
http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=SF0DVEvV34G4GnXEDU4SXniaDebJ_UvC1G1IuikVz3vpOJrBpyD.VqCJCVJHMh99He3iFz1Rzoqxb0b7Z.0-
Now since yahoo's api is discontinued I have decided to switch over to Google Place. But I cannot find an Url to use. I just dowlod the framework and use the api key. Where can I find such url for Google Place.
Register for the Google Places API by following the linke provided below:
https://code.google.com/apis/console
Refer Code Link for places Auto Search
https://github.com/AdamBCo/ABCGooglePlacesAutocomplete
NSString *const apiKey = #"*****23xAHRvnOf2BVG8o";
NSString * searchWord = #"search some place "
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
pragma mark - Network Methods
-(void)retrieveGooglePlaceInformation:(NSString *)searchWord withCompletion:(void (^)(BOOL isSuccess, NSError *error))completion {
if (!searchWord) {
return;
}
searchWord = searchWord.lowercaseString;
self.searchResults = [NSMutableArray array];
if ([self.searchResultsCache objectForKey:searchWord]) {
NSArray * pastResults = [self.searchResultsCache objectForKey:searchWord];
self.searchResults = [NSMutableArray arrayWithArray:pastResults];
completion(YES, nil);
} else {
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&radius=500&language=en&key=%#",searchWord,apiKey];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jSONresult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
if (error || [jSONresult[#"status"] isEqualToString:#"NOT_FOUND"] || [jSONresult[#"status"] isEqualToString:#"REQUEST_DENIED"]){
if (!error){
NSDictionary *userInfo = #{#"error":jSONresult[#"status"]};
NSError *newError = [NSError errorWithDomain:#"API Error" code:666 userInfo:userInfo];
completion(NO, newError);
return;
}
completion(NO, error);
return;
} else {
NSArray *results = [jSONresult valueForKey:#"predictions"];
for (NSDictionary *jsonDictionary in results) {
}
//[self.searchResultsCache setObject:self.searchResults forKey:searchWord];
completion(YES, nil);
}
}];
[task resume];
}
}
I am have been trying to send a post request from one of my ViewControllers which collects some data from another ViewController through
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Anyways when I try to send the post request to send variables to the database, the last two parameter values are stored as 0. I have already converted the float variables to to NSStrings. I checked to make sure the values were correct before sending the post request through NSLog. However it is still showing up as zero. When I enter the form manually through my web browser with the exact same values, they are stored in the database.
My Post request looks like the following:
NSString *dateString = _date.text;
NSString *times = _time.text;
NSString *shape_illuminated = _shape.text;
NSString *orientation_illuminatedString = _orientation.text;
NSLog(#"compass is:%#%#", compassReceived, rollReceived);
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSURL * url = [NSURL URLWithString:#"http://example.com/mobile_app/submit.php"];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:url];
NSString * params = [NSString stringWithFormat:#"date=%#&time=%#&shape_illuminated=%#&orientation_illuminated=%#&=compass_direction=%#&=degreehorizon=%#", dateString, times, shape_illuminated, orientation_illuminatedString, compassReceived, rollReceived];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"Response:%# %#\n", response, error);
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"HttpReponseData = %#",text);
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Account error"
message:[NSString stringWithFormat:#"%#", dictionary]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}];
[dataTask resume];
Some of the values from those NSLogs are:
compass is:127.0Angle:99.485866
In mysql I have the fields set up as floats.
Any ideas?
UPADATE:
I tried hard coding the values for compass_direction and degreehorizon and it still did not work.
UPDATE:
I have now tried properly configuring the url parameters using the following method:
-(NSString *)urlenc:(NSString *)val
{
CFStringRef safeString =
CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)val,
NULL,
CFSTR("/%&=?$#+-~#<>|,.()[]{}^!"),
kCFStringEncodingUTF8);
return [NSString stringWithFormat:#"%#", safeString];
}
and then adding:
SString *query =
[NSString stringWithFormat:#"shape_illuminated=%#&orientation_illuminated=%#&=compass_direction=%#&=degreehorizon=%#",
[self urlenc:shape_illuminated],
[self urlenc:orientation_illuminatedString],
[self urlenc:convertedCompass],
[self urlenc:convertedRoll]];
NSLog(#"%#", query);
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content- type"];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
but those two values are still not being sent. The output from the nslog looks fine to me. I even made a php form and entered the same values on my computer which works great, so I do not know what is going on here.
I am retrieving JSON information for an API and it says on the API that it is in JSON but I noticed it is in JSONP or "json with padding" as some call it. I tired to look everywhere to find how to parse this but no luck. The information I am trying to receive is this:
({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":
{"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and
every evil work."}}}],"direction":"LTR","type":"verse"});
The link to the data is https://getbible.net/json?p=James3:16, so you can look at it directly.
This is the code I am using to try to retrieve the JSON Data and parse it into a NSMutableDictionary.
-(void)fetchJson {
NSString *currentURL = [NSString stringWithFormat:#"https://getbible.net/json?p=James"];
NSURL *url = [NSURL URLWithString:currentURL];
NSData *data = [[NSData alloc]initWithContentsOfURL:url];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
NSMutableData *receivedData = [[NSMutableData alloc] initWithLength:0];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
[receivedData setLength:0];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:#".json" expectedContentLength:-1 textEncodingName:nil];
expectedTotalSize = [response expectedContentLength];
if ([data length] !=0) {
NSLog(#"appendingData");
[receivedData appendData:data];
if(connection){
NSLog(#"Succeeded! Received %lu bytes of data",(unsigned long)[receivedData length]);
}
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if(jsonResponse){
NSArray *responseArr = [jsonResponse mutableCopy];
NSLog(#"%lu",(unsigned long)[responseArr count]);
}else if (!jsonResponse){
//do internet connection error response
}
}
}
The results I am getting back from putting a breakpoint in the code is:
jsonResponse returns NULL
NSError NSCocoaErrorDomain code - 3840
but my NSData *data is returning 15640 bytes.
My console is displaying this from the NSLogs I used for debugging:
2014-04-20 01:27:31.877 appendingData
2014-04-20 01:27:31.879 Succeeded! Received 15640 bytes of data
I am receiving the data correctly but I am not parsing it correctly I know the error is because the JSON is in JSONP format. If anyone could please help with this I would appreciate it so much. I have tired to give as much detail on this question as I can but if you need more information just let me know so I can add it and make this as clear as possible.
Your code has at least two separate attempts to download the data. Neither is really correct. The code also only works with JSON, not JSONP.
Try this:
NSURL *url = [NSURL URLWithString:#"https://getbible.net/json?p=James"];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSRange range = [jsonString rangeOfString:#"("];
range.location++;
range.length = [jsonString length] - range.location - 2; // removes parens and trailing semicolon
jsonString = [jsonString substringWithRange:range];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *jsonError = nil;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&jsonError];
if (jsonResponse) {
// process jsonResponse as needed
} else {
NSLog(#"Unable to parse JSON data: %#", jsonError);
}
} else {
NSLog(#"Error loading data: %#", error);
}
}];
One problem is that the data you're downloading has extraneous information at the beginning and end. The JSON being delivered by your URL is:
({"book":[{"book_name":"James","book_nr":"59","chapter_nr":"3","chapter":{"16":{"verse_nr":"16","verse":"For where envying and strife is, there is confusion and every evil work."}}}],"direction":"LTR","type":"verse"});
As the error message you're seeing indicates: you need to remove the initial ( from the beginning of the string and the ); from the end so that your JSON will start with the dictionary that your code expects. You can do this by calling subdataWithRange: on your NSData object:
NSData* jsonData = [data subdataWithRange:NSMakeRange(1, data.length-3)];
NSDictionary* jsonResponse = [NSJSONSerialization JSONObjectWithData:jsonData
options:0
error:&error];
Just to update everyone, the NSURLRequest has been deprecated in iOS9. I tried the answer by #rmaddy, and I didn't receive anything either (just like what #lostAtSeaJoshua was encountering I guess). I have updated rmaddy's answer to reflect the NSURLSession implementation that has (I think) replaced NSURLRequest:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:#"http://somerandomwebsite.com/get.php?anotherRandomParameter=5"];
[[session dataTaskWithURL:url
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
if (data) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"stringJSONed: %#",jsonString);
//Do something with the received jsonString, just like in # rmaddy's reply
} else {
NSLog(#"Error loading data: %#", error);
}
}] resume];
Just a heads up notice, when I first ran it, it gave me the security error. What you need to do (if you are using http) is to add this to your plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I have to mention that after the NSAllowArbitraryLoads key, there are most probably other keys and values, such as NSExceptionDomain. But they're not really relevant to this answer I think. If you need to look further, let me know and I will dig deeper :)