I use this type of function to call my Web Services from my iOS App:
-(NSData *)getMainMenuJsonData{
NSString *urlAsString = [NSString stringWithFormat:#"%#%#/%#/%#",[configs valueForKey:#"wsURL"], [configs valueForKey:#"clientToken"], [configs valueForKey:#"appToken"], [CommonsUtils getCommonUtil].getAppLanguage];
NSString* webStringURL = [urlAsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:webStringURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2.5];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
NSUInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
if (statusCode == 200 && error == Nil) {
if (error != Nil) {
return Nil;
} else {
return data;
}
}
else {
return Nil;
}
}
As far as I understand, I'm sending something like:
http://myserver.com/WSFunction/Parameter1/Parameter2 and so on.
This is a problem for me because if I need to add 1 parameter I have to create another service with almost the same functionality but using the new parameter i.e:
http://myserver.com/WSFunction/Parameter1/Parameter2/Parameter3
My idea is to call always the same Web Service but passing the version of the Service and a JSON with all the parameters that I need, but as far as I know, I have to warp the JSON in the message (maybe this is not the right word, and that is maybe why I can't find anything in Google), so at the end I will finish with something like:
http://myserver.com/WSVersionNumber/[SerializedJSON]
And in the server I will have a logic like this:
Case:"Version 1"
Deserialize JSON and use Parameter1 and Parameter2
Case:"Version 2"
Deserialize JSON and use Parameter1 and Parameter2 and Parameter3
At the end what I want is to do something like this in iOS:
-(NSData *)getMainMenuJsonData{
NSString *urlAsString = #"http://myserver.com/WSVersionNumber/"
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:webStringURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2.5];
[urlRequest setParameterName:#"SerializedJSON" withValue:SerializedJSONVar];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
NSUInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
if (statusCode == 200 && error == Nil) {
if (error != Nil) {
return Nil;
} else {
return data;
}
}
else {
return Nil;
}
}
Is there a way to do something like this in iOS?, I'm not interested in doing this with a third party library because is the enterprise policy.
Thanks in advance.
Please check, may be this link will helps you,
This example use ASIHTTPRequest, very easy and effective way to deal with JSON.
https://stackoverflow.com/a/7928734/4852079
Thanks to the answer posted by Tejas Patel that lead me to the right information.
The code ended like this:
-(NSData *)getLogin:(NSMutableDictionary *)pData {
NSString *tUrlAsString = [NSString stringWithFormat:#"%#%#/%#/%#/GetLogin",[_Configs valueForKey:#"wsURL"], [_Configs valueForKey:#"wsVersion"], [_Configs valueForKey:#"clientToken"], [CommonsUtils getCommonUtil].getAppLanguage];
NSString* tWebStringURL = [tUrlAsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest * tUrlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:tWebStringURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2.5];
[tUrlRequest setHTTPMethod:#"POST"];
[tUrlRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSData *tJsonData;
if ([NSJSONSerialization isValidJSONObject:pData]) {
NSError *tError;
tJsonData = [NSJSONSerialization dataWithJSONObject:pData options:NSJSONWritingPrettyPrinted error:&tError];
if (!tJsonData) {
NSLog(#"json Data %#",tError.description);
return Nil;
} else {
NSString *tJSONString = [[NSString alloc] initWithBytes:[tJsonData bytes] length:[tJsonData length] encoding:NSUTF8StringEncoding];
NSLog(#"JSON String %#",tJSONString);
[tUrlRequest setHTTPBody:tJsonData];
NSURLResponse * tResponse = nil;
NSData * tData = [NSURLConnection sendSynchronousRequest:tUrlRequest returningResponse:&tResponse error:&tError];
NSUInteger tStatusCode = ((NSHTTPURLResponse *)tResponse).statusCode;
if (tStatusCode == 200 && tError == Nil) {
if (tError != Nil) {
return Nil;
} else {
return tData;
}
} else {
return Nil;
}
}
} else {
NSLog(#"This Data can't be serialized");
return Nil;
}
}
At the end you just have to add a JSON inside the setHTTPBody, so in your REST service you have to receive all the nodes of your JSON with the same name for the variables.
I hope this helps someone in the future.
Regards.
Related
Code:
+(id)loadJSONDataFromURL:(NSString *)urlString{
MsgsHelper *msg=[[MsgsHelper alloc]init];
// This function takes the URL of a web service, calls it, and either returns "nil", or a NSDictionary,
// describing the JSON data that was returned.
NSError *error;
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
// Call the web service, and (if it's successful) store the raw data that it returns
NSURLResponse *response = nil; //
NSData *data = [ NSURLConnection sendSynchronousRequest:request returningResponse: &response error:&error ];
//here we get the respond from NSURLResponse and then we check for the statusCode //1
//200 is ok,, 0 is no internet connection else is server error //2
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;//1
//any warning like this one with integers shuold add the casting (int)
int statCode = (int)[httpResponse statusCode];//2
if(statCode == 200){
if (!data)
{
//NSLog(#"Download Error: %#", error.localizedDescription);
return nil;
}
// Parse the (binary) JSON data from the web service into an NSDictionary object
id dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (dictionary == nil) {
//NSLog(#"JSON Error: %#", error);
return nil;
}
return dictionary;
}else if(statCode == 0){
[msg alertStatus:NSLocalizedString(#"No internet", #"Message") :#"" :0 ];
return nil;
}else{
//Server Error
//NSLog(#"Server Error");
[msg alertStatus:NSLocalizedString(#"Server Error", #"Message") :#"" :0 ];
return nil;
}}
the problem on result as below:
results: (
{
"Name" = "\U0633\U0639\U0648\U062f \U0639\U0628\U062f\U0627\U0644\U0639\U0632\U064a\U0632 \U064a\U0646 \U062c\U062f\U064a\U062f";
"location" = CENTER;
}
)
maybe need to dataUsingEncoding to read arabic string, but how do it.
Thanks,
i am using Gdata and already suceessfully login gmail and call below method
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth{
if (error != nil) {
}
else{
// i got successful login here
self.auth=auth;
}
}
in above method i got Authentifacation token and etc.
Now
NSString *urlStr = #"https://www.google.com/m8/feeds/contacts/default/full";
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[self.auth authorizeRequest:request
completionHandler:^(NSError *error) {
NSString *output = nil;
if (error) {
output = [error description];
} else {
// Synchronous fetches like this are a really bad idea in Cocoa applications
//
// For a very easy async alternative, we could use GTMHTTPFetcher
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (data) {
// API fetch succeeded
output = [[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding] autorelease];
} else {
// fetch failed
output = [error description];
}
}
}];
but i got error 401
please helpful me
I'm working on an update of an iOS app that another developer created. He was using ASIHTTPRequest to handle http requests. However, the version of the app I have to work with crashes. Since ASIHTTPRequest is not being updated anymore, I thought then that I should switch to using AFNetworking, but it's far too complicated for me to figure out.
So finally I thought I could just use NSURLConnection on its own.
Here is the original code that I'm trying to replace:
-(NSArray*)readUTF16LEFeed:(NSURL*) urlToRead{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:urlToRead];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
lastModified = [NSDate date];
NSData *response = [request responseData];//UTF-16LE
NSString* responseString = [[NSString alloc] initWithData:response encoding:NSUTF16LittleEndianStringEncoding];
DLog(#"string is: %#",responseString);
responseString = [responseString stringByReplacingOccurrencesOfString:#"ISO-8859-1" withString:#"UTF16-LE"];
NSData* data = [responseString dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
return [self parseNamesFromXML:data];
}
return nil;
}
And here is what I'm trying to use instead:
-(NSArray*)readUTF16LEFeed:(NSURL*) urlToRead{
NSURLRequest *request = [NSURLRequest requestWithURL:urlToRead];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error ) {
if ([data length] >0 && error == nil)
{
// DO YOUR WORK HERE
lastModified = [NSDate date];
// NSData *response = [request responseData];//UTF-16LE
NSString* responseString = [[NSString alloc] initWithData:data encoding: NSUTF16LittleEndianStringEncoding];
DLog(#"string is: %#",responseString);
responseString = [responseString stringByReplacingOccurrencesOfString:#"ISO-8859-1" withString:#"UTF16-LE"];
NSData* data = [responseString dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
return [self parseNamesFromXML:data];
}
else if ([data length] == 0 && error == nil)
{
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error = %#", error);
}
}];
return nil;
}
However, I'm getting the error "Incompatible block pointer types sending NSArray to parameter of type void. And also "Control may reach end of non-void block."
How can I get this to work?
Make return type of your method as void. Dont return anything. Just call [self parseNamesFromXML:data]; method.
-(void)readUTF16LEFeed:(NSURL*) urlToRead{
NSURLRequest *request = [NSURLRequest requestWithURL:urlToRead];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error ) {
if ([data length] >0 && error == nil)
{
// DO YOUR WORK HERE
lastModified = [NSDate date];
// NSData *response = [request responseData];//UTF-16LE
NSString* responseString = [[NSString alloc] initWithData:data encoding: NSUTF16LittleEndianStringEncoding];
DLog(#"string is: %#",responseString);
responseString = [responseString stringByReplacingOccurrencesOfString:#"ISO-8859-1" withString:#"UTF16-LE"];
NSData* data = [responseString dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
[self parseNamesFromXML:data];
}
else if ([data length] == 0 && error == nil)
{
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error = %#", error);
}
}];
}
In parseNamesFromXML method, process your results and assign results array to a property. You can access it where ever you want.
-(void) parseNamesFromXML:(NSData *)xmlData
{
NSError *error;
//DLog(#"data is %#", [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding ]);
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
if (doc)
{
self.dataArray = [self parseXmlDoc:doc];
}
}
The completion block is called asynchronously, so by the time your code reaches return [self parseNamesFromXML:data]; the methods scope is already done (meaning the method returned nil.
Try using [NSURLConnection sendSynchronousRequest:returningResponse:error:] instead, since the original code is also synchronous.
Edit:
As Julian Król suggested, if you return something within a block, it will be counted as the return value of this block, not of the original method. But since the block does not have a return value, you'll get the compiler error.
I have a NSURLConnection (two of them), and they're running in the wrong order.
Here's my method:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
}
}
else {
NSLog(#"error: %#", error);
}
}];
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
- (NSString *)percentEscapeString:(NSString *)string
{
NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
(CFStringRef)#" ",
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
return [result stringByReplacingOccurrencesOfString:#" " withString:#"+"];
}
So, it's two NSURLConnection's that are added to the [NSOperationQueue mainQueue]. What my output is showing me is that the second NSURLConnection is running before the first one. So it tries to go to the page where I download data before I'm logged in, so it (obviously) returns a "You're not logged in" error.
How do I schedule them one after another?
The issue, as I suspect you have realized, is that you're doing asynchronous network requests (which is good; you don't want to block the main queue), so there's no assurance of the order they'll finish.
The quickest and easiest answer is to simply put the call to the second request inside the completion block of the first one, not after it. You don't want to be making that second one unless the first one succeeded anyway.
To keep your code from getting unwieldy, separate the login from the request for main page. And you can use the completion block pattern which is common with asynchronous methods. You add a parameter to loginToMistarWithPin that specifies what it should do when the request finishes. You might have one completion block handler for success, and one for failure:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password success:(void (^)(void))successHandler failure:(void (^)(void))failureHandler {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
// assuming you validated that everything was successful, call the success block
if (successHandler)
successHandler();
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
if (failureHandler)
failureHandler();
}
}
else {
NSLog(#"error: %#", error);
if (failureHandler)
failureHandler();
}
}];
}
- (void)requestMainPage {
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[requestHome setHTTPMethod:#"GET"]; // this looks like GET request, not POST
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
Then, when you want to login, you can do something like:
[self loginToMistarWithPin:#"1234" password:#"pass" success:^{
[self requestMainPage];
} failure:^{
NSLog(#"login failed");
}];
Now, change those successHandler and failureHandler block parameters to include whatever data you need to pass back, but hopefully it illustrates the idea. Keep your methods short and tight, and use completion block parameters to specify what an asynchronous method should do when it's done.
Can you check the below link. It is about forcing one operation to wait for another.
NSOperation - Forcing an operation to wait others dynamically
Hope this helps.
I'm working on an iPad app that requests data from a server, changes and submits it, and then re-requests the data from the server, displaying it to the user. The app updates the data just fine (the equivalent web app sees the update happening), but the data that the iPad app gets back is the old data. I thought maybe it was the caching flag on the NSURLRequest, but it doesn't look like it.
Here is my sequence of calls:
NSString* currentStuff = self.fCurrentIndex.currentStuff;
NSError* err = nil;
[self.fCurrentIndex approve:currentStuff withUsername:username andPassword:password error:&err];
if (err == nil)
{
// rebuild the case list (grab the data from the URL again first)
[self getCaseListViaURL]; // grab the updated data
[self setupUIPanel]; // display it
}
Here's the code that grabs the data (the 'getCaseListViaURL' call):
NSURLResponse* response;
NSError* err = nil;
NSMutableDictionary * jsonObject = nil;
NSString * urlRequestString;
urlRequestString = [method to get the URL string];
NSURL * url = [NSURL URLWithString:urlRequestString];
NSURLRequest * request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60];
NSData * data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&err];
if (err == nil)
{
jsonObject = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&err];
}
if (err && error) {
*error = err;
}
return jsonObject;
Is there any way to force the server to serve up the updated data?
Thanks in advance.
EDIT: Per comments, I'm adding the sequence of update to the server and subsequent pull:
This does the push to the server:
NSString* currentStuff = self.fCurrentIndex.currentStuff;
NSError* err = nil;
[self.fCurrentPatient approveStuff:currentStuff withUsername:username andPassword:password error:&err];
Where 'approveStuff' eventually calls:
__block NSData * jsonData;
__autoreleasing NSError * localError = nil;
if (!error) {
error = &localError;
}
// Serialize the dictionary into JSON
jsonData = [NSJSONSerialization dataWithJSONObject:data
options:NSJSONWritingPrettyPrinted
error:error];
if (*error) return nil;
NSURLResponse* response;
NSString * urlRequestString;
urlRequestString = [self urlStringForRelativeURL:relativeURL
withQueryParams:params];
NSURL * url = [NSURL URLWithString:urlRequestString];
NSMutableURLRequest * request;
request = [NSMutableURLRequest requestWithURL:url
cachePolicy:self.cachePolicy
timeoutInterval:self.timeOutInterval];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData];
[request setValue:#"application/json;charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
jsonData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&localError];
NSMutableDictionary * jsonObject;
if (localError == nil)
{
jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&localError];
}
if (error && localError) {
*error = localError;
}
return jsonObject;
Right after this, I call the aforementioned get call and rebuild the UI. Now, if I stick a breakpoint when I do the get, and check on the web server if the data is updated after the push, I see the data is there. However, when I let the get operation continue, it gives me the old data.
So it looks like the issue was on the server. There were some data structures on the server side that weren't being refreshed when the data was being posted.