Using AFNetworking for Web Service data retrieval - ios

Learning steeply on this one so I'd say it's 99% my mistake somewhere.
Trying a simple procedure for my first app.
I want to get data from the web service and output something to a label.
I'm a .NET developer so web service has been done and returns JSON data as expected in a browser.
{"GetVenuesForBrandResult":[{"brandID":0,"createdDate":"/Date(-62135596800000+0000)/","venueID":2,"venueName":"HMV Records"}]}
After looking at SO, decided to use AFNetworking, version 2.0, loaded as a pod.
So everything works in terms of getting the data but 2 questions please;
1) the output view in Xcode shows data thus;
GetVenuesForBrandResult=({brandID = 0;createdDate = "/Date(-62135596800000+0000)/";venueID = 2;venueName = "HMV Records";
}
);
Is this just XCode5 formatting for display purposes? And will my data be json in real terms?
2) How do I consume this?
Tried checking for isKindOfClass to be an nsarray or nsdictionary and these if conditions are never met. Which led me to suspect Q1 is the issue.
Your help to an Xcode newbie is appreciated.
as per Paul's excellent hint, the relevant part of my code is here;
the web service link is hard coded for now for test purposes
NSString *urlstring = #"http://hiddenurl/shopBeaconService/Service1.svc/GetVenuesForBra nd/1";
//NSString *str2 = self.email.text;
//NSString *str3 = #"/";
//NSString *str4 = [self.password text];
//NSString *urlstring = [NSString stringWithFormat:#"%#%#%#%#",str1,str2,str3,str4];
NSURL *url = [NSURL URLWithString:urlstring];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation,id responseObject)
{
NSLog(#"%#",responseObject);
// NSDictionary *item = [responseObject objectForKey:#""];
// NSString *title = [item objectForKey:#"venueName"];
//NSLog(#"venue= %#",title);
} failure:nil];
[operation start];

The response object when you use the AFJSONResponseSerializer is an NSDictionary. What you are seeing in your log is the output from [responseObject description] - a method that prints a description for logging purposes.
Ray Wenderlich has a good tutorial on AFNetworking

Related

how to handle space in sending json parameters to server in ios?

I am sending data to the server it is going successful but response coming with %20 at the space in data what I have sent to server here is the code I am using
NSString *str = [NSString stringWithFormat:#"http://www.me911.com/new/miphone3/android_edithealth.php?profile_id=%#&health_condition=%#&health_insurance_provider=%#&primary_physician_name=%#&primary_physician_phone=%#&last_physical=%ld&blood_type=%#&organ_donor=%#",profileId,txthospital.text,textinsurence.text,txtprimary.text,txtphone.text,dateInMillis,questionNo,textorgan.text];
str = [str stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSLog(#"Healthinfo URL: %#",str);
NSMutableURLRequest *dataRqst = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:str] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
[dataRqst setHTTPMethod:#"POST"];
NSString *stringBoundary = #"0xKhTmLbOuNdArY---This_Is_ThE_BoUnDaRyy---pqo";
NSString *headerBoundary = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",stringBoundary];
[dataRqst addValue:headerBoundary forHTTPHeaderField:#"Content-Type"];
NSMutableData *postBody = [NSMutableData data];
[dataRqst setHTTPBody:postBody];
NSHTTPURLResponse *dataresponse =[[NSHTTPURLResponse alloc] init];
NSError* error = [[NSError alloc] init] ;
//synchronous filling of data from HTTP POST response
NSData *responseData = [NSURLConnection sendSynchronousRequest:dataRqst returningResponse:&dataresponse error:&error];
//convert data into string
NSString *responseString = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:NSUTF8StringEncoding];
NSLog(#"responseString %# ",responseString);
if (responseString == NULL)
{
NSDictionary *infoDic = [[NSDictionary alloc] initWithObjectsAndKeys:#"There was a small problem",
#"title",
#"The network doesn't seem to be responding, please try again.",
#"message",
#"OK",
#"cancel",
#"1",
#"tag",nil,
#"delegate", nil];
[CommonFunctions performSelectorOnMainThread:#selector(showAlertWithInfo:) withObject:infoDic waitUntilDone:NO];
}
else
{
NSDictionary *jsonResponse = [responseString JSONValue];
if ([jsonResponse objectForKey:#"error"]){
NSLog(#"response %#",jsonResponse);
}
else{
}
NSMutableArray *dataresponse=[jsonResponse valueForKey:#"success"];
if ([jsonResponse objectForKey:#"success"])
{
NSLog(#"Array response %#",dataresponse);
}
}
And this is web service
:http://anaadit.net/miphone3/android_edithealth.php?profile_id=287&health_condition=palo%20Alto%20Veterans%20Hospital%20&health_insurance_provider=Blue%20Cross&primary_physician_name=Dr.Akki&primary_physician_phone=6504935000&last_physical=-57600&blood_type=7&organ_donor=No
Here I am sending data in textfield in like guru prasad but response getting like this guru%20prasad.
So please correct me where am I going wrong .
thanks in advance
Your code has a number of issues.
In order to create a URL with query params, I recommend to use the utility class NSURLComponents (see Apple documentation: NSURLComponents).
Composing a POST request whose content type is "multipart/formdata" is quite error prone. If you absolutely have to compose such a request I very strongly recommend to use a Network Library, for example AFNetworking.
On the other hand, using a POST request whose Content-Type is application/json is very easy to setup, especially with NSURLSession and friends.
You can find specific solutions for any of the suggested approaches mentioned above on SO, too.
Its seems there is some problem in parsing data, the space are replaced with %20...It seems you are using NSURL Connection for making API Calls.
Use AFNetworking for making API Calls, the response data will automatically come in JSON format and you can initialize Dictionary from same.
Please find below link for AFNetworking:
https://github.com/AFNetworking/AFNetworking
Please use "AFNetworking" and the code will be:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"profile_id": #"287", #"health_condition": #"palo Alto Veterans Hospital", #"health_insurance_provider": #"Blue Cross",
#"primary_physician_name":#"Dr.Akki",
#"primary_physician_phone":#"6504935000",
#"last_physical":#"-57600",
#"blood_type":#"7",
#"organ_donor":#"No"};
[manager POST:#"http://anaadit.net/miphone3/android_edithealth.php" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
Please try the above code. I think this will help you.

How to get the HTML of a website and parse it with NSXMLParser

I am trying to parse the XML from a website (e.g. "http://www.kick-girl.com/?cat=3")
I can easily get the XML using:
NSURL *url = [NSURL URLWithString:#"http://www.kick-girl.com/?cat=3"];
NSLog(#"%#", [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]);
However when I try to use NSXMLParser it simply does not work. I have already tried converting the string to data
NSString *s = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
NSData *d = [s dataUsingEncoding:NSUTF8Encoding];
NSXMLParser *xmlparser = [NSXMLParser alloc] initWithData:d];
xmlparser.delegate = self;
[xmlparser parse];
And it still does not work. The NSXMLParserDelegate methods do not get called.
e.g.
- (void)parser:didStartElement:namespaceURI:qualifiedName:attributes:
I have also tried using AFnetworking to see if that would help
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOp = [[AFHTTPRequestOperation alloc] initWithRequest:request];
requestOp.responseSerializer = [AFXMLParserResponseSerializer serializer];
[requestOp.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:#"application/xml", #"text/xml", #"text/html", nil]];
[requestOp setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSXMLParser *xmlparser = responseObject;
xmlparser.delegate = self;
[xmlparser parse]; //Delegate methods are not called for some reason...
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"OOPS: %#", error);
}];
[requestOp start];
I don't understand what is the problem.
I get a parse error from -parser:parserErrorOccurred:
Error Domain=NSXMLParserErrorDomain Code=65 "The operation couldn’t be completed. (NSXMLParserErrorDomain error 65.)" UserInfo=0x8da6ce0 {NSXMLParserErrorLineNumber=2, NSXMLParserErrorColumn=17, NSXMLParserErrorMessage=attributes construct error
}
How do I fix this?
Apparently the XML that I got from the webpage is not perfect and has some weird stuff in it. Although a web browser is quite forgiving when it comes to reading xml, the nsxmlparser is very strict.
To prevent that weird stuff from coming out I simply took a substring of the parts that I wanted, then removed whitespaces like new lines carriage returns and tabs. And did some string manipulations to make sure the tags were actually balanced.

How to use LinkedIn Share Api in iOS 7

I'm adding the ability to share an article on LinkedIn in an iOS 7 app using oauth2. I've gotten thru the authentication and have the access token. The documentation seems to be pretty clear about that, but it's odd, to actually post, things get pretty vague. I know I post here: http://api.linkedin.com/v1/people/~/shares appending the token.
But every example then just has the same code using OAMutableRequest, building the dictionary,etc. but they never explain what that is, how to incorporate that library or anything, its just strange. Is this the accepted best practice, the library hasn't been updated in 3 years so it has errors for arc and other things. All the code examples mention the same "consumer" property with no discussion of how or why that's needed. I can't seem to find how you build the post request with the parameters linkedin needs to post something on the site. Is OAMutableRequest the only way? If so, how have people updated it to work? Thanks so much!
After retrieve your Access Token you can use AFNetworking for a POST request like this example code:
NSString *stringRequest = #"https://api.linkedin.com/v1/people/~/shares?oauth2_access_token=ACCESS_TOKEN&format=json";
//Request parameter on a dictionary (keys in camel case)
NSDictionary *update = [[NSDictionary alloc] initWithObjectsAndKeys:
[[NSDictionary alloc] initWithObjectsAndKeys: #"anyone",#"code",nil], #"visibility",
#"comment to share", #"comment",
[[NSDictionary alloc] initWithObjectsAndKeys:#"description share", #"description",
#"link_url", #"submittedUrl",
#"title share",#"title",
#"image_url",#"submittedImageUrl",nil],
#"content",nil];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFJSONRequestSerializer *requestSerializer = [AFJSONRequestSerializer serializer];
[requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Accept"];
manager.requestSerializer = requestSerializer;
[manager POST:stringRequest parameters:update success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"result: %#", responseObject);
completionBlock(YES, responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DDLogError([error localizedDescription]);
completionBlock(NO, nil, error);
}];
Important: the keys of the dictionary are in camel case according to Linkedin API.
In the case linkedin give bad request (error 400), another way to create the dictionary is:
NSMutableDictionary *update = [[NSMutableDictionary alloc] init];
if(message)
{
//Set visibility
NSDictionary *visibility = [[NSDictionary alloc] initWithObjectsAndKeys:#"anyone", #"code", nil];
[update setObject:visibility forKey:#"visibility"];
//Set comment
[update setObject:message forKey:#"comment"];
//Set content or append imageUrl/postUrl to message to share
NSMutableDictionary *content = [[NSMutableDictionary alloc] init];
if(postUrl)
[content setObject:imageUrl forKey:#"submittedUrl"];
if(imageUrl)
[content setObject:imageUrl forKey:#"submittedImageUrl"];
if(postUrl || imageUrl)
[update setObject:content forKey:#"content"];
}
Andr3a88's answer might work, but I could never get everything figured out with linkedin's side. Luckily, they finally released a full sdk: https://developer.linkedin.com/docs/share-on-linkedin, https://developer.linkedin.com/docs/ios-sdk

Parsing JSON Help IOS

I currentley have a jSON file which is created a by a script which can be seen here: http://ddelay.co.uk/bus/output.json
I currently have managed to setup the following to grab the JSON which works, i just cannot figure how to grab the data to assign it :/
-(void)makeStopRequests{
NSURL *url = [NSURL URLWithString:#"http://ddelay.co.uk/bus/output.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking Asynchronous Task
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id responseObject)
{
NSLog(#"JSON RESULT %#", responseObject);
self.stopArray = [responseObject objectForKey:#"stop_name"];
[self.tableView reloadData];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id responseObject)
{
NSLog(#"Request Failed: %#, %#", error, error.userInfo);
}];
[operation start];
}
I hope somebody can guide me through how I would select the data,
Thank you.
Damien
I would recommend just using something as simple as NSJSONSerialization, which is built into iOS after iOS 5 (may have been earlier, but not too sure).
Just get the data from the request and then parse that into a dictionary with the following code
NSDictionary *dict= [NSJSONSerialization JSONObjectWithData:webData options:NSJSONReadingMutableLeaves error:nil];
get the data using the standard built in networking structure as well
NSURLRequest *someRequest = [[NSURLRequest alloc] initWithURL:someURL cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:10.0];
NSURLConnection *someConnection= [[NSURLConnection alloc] initWithRequest:someRequest delegate:self];
And then do whatever appropriate delegation and data processing. Read the documentation to get more details
You are using AFNetworking and it provides you the response object. That is already being cast to Objective c object.
You can simply assign it to NSArray or NSDictionary like this
NSArray *array = (NSArray*)responseObject;//if your json returns an array
NSDictionay *dict (NSDictionary*)responseObject;//if json returns dictionary.
This url returns JSON in this format;
[ {
"service":"22",
"provider":"First in Yorkshire",
"dest":"Nether Edge to Woodhouse",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Woodhouse"
},
{
"service":"22",
"provider":"First in Yorkshire",
"dest":"Barnsley to Rotherham",
"dest URL":"/web/public_service_stops.asp?service=22&operatorid=31&systemid=30&goingto=Rotherham"
}
]
Which actually is an array of dictionaries so to access data you have to loop like this.
NSArray *jsonResponse = (NSArray*)responseObject;
for (NSDictionary *dic in jsonResponse){
NSString *service = [dic valueForKey:#"service"];
NSString *provider = [dic valueForKey:#"provider"];
//Same for others
//It will better you create calss with these properties and then add that object to an Array, and on reloading data in table get that object from array in cellForRowAtIndexPath and use requried property to populate your data.
//Or if you just want to use stop name add stop name to array and use it.
NSString *stopName =[dic valueForKey:#"stop_name"];
//[dataArray addObject:stopName]; in case you want to use only stop name
}
Here's minimal code:
self.stopArray = responseObject;
for (NSDictionary *stopDict in self.stopArray) {
NSString *service = [stopDict objectForKey:#"service"];
NSString *provider = [stopDict objectForKey:#"provider"];
NSString *dest = [stopDict objectForKey:#"dest"];
NSString *destURL = [stopDict objectForKey:#"dest URL"];
// do something with this data
}
For anything more complex than this, you'll probably want to make your own data class, so you don't have to parse through dictionaries and arrays in your table view delegate methods.

Posting a serialized object with AFNetworking failing

I have a a data object, called DataElement. It contains a string of Base64 converted image bytes, along with a couple of other fields.
I am trying to post this to my wcf service and am getting an error 'Expected status code in (200-299), got 400.
The goal is to post data + an image to the WCF (rest) service, and get a modified image back- an end to end test of what I am working on.
In my post method, if I leave the encoded string empty on the object everything works just fine- but if that string is anything other than empty I get this error.
My WCF service isn't even being hit, it just bombs right to the error. Here is my post method... what am I doing wrong?
- (void)postDataToServer:(NSString*)server dataElement:(DataElement*)dataElement asJson:(BOOL)useJson
{
NSString *urlString = [[NSString alloc] init];
NSData *encodedData;
urlString = [[server copy] stringByAppendingString:#"EchoXml"];
encodedData = [self encodeDataElementAsXml:dataElement];
NSURL *url = [NSURL URLWithString:urlString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:urlString parameters:nil];
[request setHTTPBody:encodedData];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[_responseTextView setText:[NSString stringWithFormat:#"Successfully uploaded file to %#", urlString]];
NSObject *httpResponseObject;
httpResponseObject = [self parseResponseAsXml:responseObject];
if ([httpResponseObject isKindOfClass:[DataElement class]])
{
DataElement *dataElement = (DataElement *)httpResponseObject;
_responseTextView.text = dataElement.DataText;
if (dataElement.DataImageBase64 != nil)
{
UIImage *dataImage = [self getImageFromString:dataElement.DataImageBase64];
self.responseImageView.image = dataImage;
}
}
NSLog(#"Successfully uploaded file to %#", urlString);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// It goes here immediately
[_responseTextView setText:[NSString stringWithFormat:#"Error: %#", error]];
NSLog(#"Error: %#", error);
}];
[operation start];
}
Edit: Sorry the formatting got wonky when I pasted it in...
The important parts of your code are:
NSString* urlString = [server stringByAppendingString:#"EchoXml"];
NSURL *url = [NSURL URLWithString:urlString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient
requestWithMethod:#"POST" path:urlString parameters:nil];
The actual URL that AFNetorking requests is the AFHTTPClient's base URL, with the specified path appended to it.
Your mistake is that you are specifying the same urlString again.
So, if urlString is http://your.server.com/EchoXml, then the effective URL that you're requesting is http://your.server.com/EchoXmlhttp://your.server.com/EchoXml. As you see, that doesn't work.
Fix your base URL and path to be something more appropriate. Since you didn't say what URL you are trying to access, it's hard to give much more detail. Maybe server should be the base URL, and EchoXml the path?
I know its bad form to answer my own question- but I found and fixed the problem. Bottom line, the code I was using above is fine- maybe not optimal (as Kurt pointed out) but it does what it is supposed to do.
The problem was on on my WCF service- REST service requests by default have a 65k upload limit. I reconfigured the service to allow large file uploads and everything is good.

Resources