Having problems using AFNetworking fetching JSON - objective-c

So i recently wanted to change from using NSUrlConnection to AFNetworking. I can receive the JSON data with both methods buts when using with AFNetworking something weird happens.
This is how it looks like with NSURLConnection
and this is how it looks like with AFNetworking
I have no idea what that (struct __lidb_autoregen_nspair) is and i dont know if that is the thing that is preventing me from displaying the data
This is the code from AFNetworking, i use the sample code from ray
-(void) fetchData{
// 1
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 2
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// 3
jsonDict = (NSMutableDictionary *)responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
// 5
[operation start];
}
----------------------------------------------------------------------------------------- Edit
-(NSMutableDictionary *) getAllGames{
[self fetchData];
DataParser *dataParserObjec = [[DataParser alloc] init];
return [dataParserObjec sendBackAllGames:jsonDict];
}

You are setting the acceptableContentTypes to text/html. I presume you are doing that because your web-service is not setting the correct Content-Type header to indicate that it's application/json. If you fixed the web service to provide the correct header Content-Type, you could then remove this acceptableContentTypes line in your Objective-C code.
If you're wondering why you didn't have to worry about that with NSURLConnection, that's because NSURLConnection doesn't do any validation of Content-Type (unless, of course, you write your own code in, for example, didReceiveResponse, that checked this).
You suggest that you are unable to display the data. But yet there it is, in your second screen snapshot. I personally would be less worried about internal representation than whether I could access the data from the NSDictionary. If you
NSLog(#"responseObject=%#", responseObject);
at #3, inside the success block, what precisely do you see? I'd wager you'll see your NSDictionary fine (despite the subtle differences in the internal representation).
My contention is that you are getting the data back successfully. Yes, your web service should set the correct Content-Type header so you don't have to overwrite the acceptableContentTypes value, but it looks like AFNetworking is retrieving your data fine.
The likely issue is that your main thread is trying to use jsonDict before the asynchronous network request is done. So the trick is to defer the use of the jsonDict until the asynchronous request is done.
You've updated your question showing us that you're instantiating a DataParser and calling sendBackAllGames. You should put that code inside the completion block of your asynchronous network request.
Alternatively, you could use a completion block pattern in your fetchData method, and then the getAllGames method could supply the sendBackAllGames code in a completion block that fetchData calls inside the success block of the AFHTTPRequestOperation.
If you used the completion block pattern, it would look like
-(void) fetchDataWithCompletionHandler:(void (^)(id responseObject, NSError *error))completionHandler {
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
// removed once we fixed the `Content-Type` header on server
//
// operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
}
And you'd call it like so:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
DataParser *dataParserObjec = [[DataParser alloc] init];
NSMutableDictionary *results = [dataParserObjec sendBackAllGames:jsonDict];
// do whatever you want with results
}
}];
If the method that called getAllGames needed the data to be returned, you'd repeat this completion block pattern for this method, too.

Related

execute block first before last line of the method

The line of code inside blocks in objective c is executed later after executing other lines of code in the same method.
My query is:
There is a method named :
-(NSDictionary*)callingWeatherService{
NSString *urlString = #"http://api.openweathermap.org/data/2.5/weather?q=London,uk";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// 3
self.dictionary = (NSDictionary *)responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// 4
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}];
// 5
[operation start];
return self.dictionary;
}
But the statement return self.dictionary; gets executed first before block execution so dictionary is returned as nil;
Is there any solution that statement return self.dictionary; is executed only after block execution. I need this approach for a specific purpose.
Don't want to use delegates, I had already used delegates inside afnetworking blocks for getting dictionary data.
The short answer is no - blocks are designed to be performed asynchronously, meaning that you never know when they complete. It would be really dumb to change this behavior.
Either use delegates, or use KVO assigned to the dictionary variable or NSNotifications. If I were you I would definitely use delegates.

Http Header Authentication on post request AFNetworking

I am trying to create a multipartFormRequestWithMethod using AFNetworking's AFHttpClient. It must authenticate with a ASP.NET REST service using Http Authorization Header. Therefore I pass username and password to the Http Authorization Header using setAuthorizationHeaderWithUsername:password:. In the body of the request I am passing a large file, several MBs. If the Header authentication fails, I want the request to get block and go in failure state before finishing the file send. There should be something that prevents the database send in case of authentication failure, but I cannot figure out what. In the current situation the AFHttpRequestOperation starts to send the file and notifies of the error only at the end of the file send.
This is the code:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://v-moxdevelop/MOX.UploadDBService/UploadDB/"]];
NSString *psw = #"psw";
NSString *userName = #"username";
[httpClient setAuthorizationHeaderWithUsername:selectedLoginItem.user password:#"psw"];
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path: [NSString stringWithFormat:#"Upload/%#/%#/%#", UDID, [selectedLoginItem.firm stringByReplacingOccurrencesOfString:#"." withString:#""], [selectedLoginItem.user stringByReplacingOccurrencesOfString:#"." withString:#""]] parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:fileData name:zipFilePath fileName:[zipFilePath lastPathComponent] mimeType:#"application/zip"];
}];
[request setTimeoutInterval:INT32_MAX];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__weak typeof(self) weakSelf = self;
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *locOperation, id responseObject) {
}failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
}];
[httpClient enqueueHTTPRequestOperation:operation];

AFNetworking getting data for XML parse error

This is my AFHTTPClient singleton:
+ (API *)sharedInstance
{
static API *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[API alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
[sharedInstance setParameterEncoding:AFJSONParameterEncoding];
[sharedInstance registerHTTPOperationClass:[AFXMLRequestOperation class]];
[sharedInstance setDefaultHeader:#"Accept" value:#"application/rss+xml"];
});
return sharedInstance;
}
And method in same class (AFHTTPClient):
- (void)requestXMLDataCompletion:(JSONResponseBlock)completionBlock
{
NSMutableURLRequest *apiRequest = [self requestWithMethod:#"GET" path:kAPIPath parameters:nil];
AFXMLRequestOperation *operation = [[AFXMLRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
// success
completionBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure
completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:#"error"]);
}];
[operation start];
}
When I call this function to get XML from RSS I get this error:
error = "Expected content type {(\n \"application/xml\",\n \"text/xml\"\n)}, got application/rss+xml";
Question:
Is whole concept of implemented singleton good and do I need any changes ?
Is there any suggestion if whole concept is wrong ?
Why am I getting this error?
Thanks.
Concept of Singleton
A singleton is more commonly known as a design pattern.
Usually a singleton is a class and behaves exactly like any other class,
the only exception being that any instances of a singleton reference the
same object data. This means that any instance of a singleton class are
actually all the same instance.
You can check out Singleton Pattern for more information and sample code to enforce how the singleton will be used.
Is there any suggestion if whole concept is wrong ?
I would suggest you to use Singleton for AFNetworking since you will have
only one instance of it.
Your Error
The error you are getting is because AFNetworking request wants Header Content-Type as "application/xml" or "text/xml"
Try changing this code:
[self registerHTTPOperationClass:[AFXMLRequestOperation class]];
to
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
I had a similar problem:
Error Domain=AFNetworkingErrorDomain Code=-1016 "Expected content type {(
"text/xml",
"application/xml"
)}, got application/rss+xml"
The answer above is not full and clear, although it helped me a lot after I read their chat. registerHTTPOperationClass doesn't help. I decided to provide some code. Solution is to NOT use this:
[AFXMLRequestOperation XMLParserRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser)
But download RSS XML using AFHTTPRequestOperation and create NSXMLParser manually:
NSString *articlesUrlString = #"http://pro.rabota.ru/feed/moscow.content.rss";
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:articlesUrlString]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData *xmlData = (NSData *)responseObject;
NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithData:xmlData];
XMLParser.delegate = self;
[XMLParser parse];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}];

objective-c post data & image

I am new to objective-c and I have been searching for a way to send a post request to my server (based on Rest URL) but also include an image with it... I have found many methods to post data... and methods to post just an image, but nothing that combines the two...
I am searching for a wrapper, class or library because it seems to be a tedious task to write all this from scratch. I found the "ASIHTTPRequest" but this is no longer supported, although Ic an turn off ARC, I would prefer to find something still supported...
I also found AFNetworking, which seems to still be supported but I could be wrong, I just cannot find a solution to combine VERY simply data and a profile image...
Any help is appreciated?
Should I just use the ASIHTTPRequest library... ?? Or does anyone have any sample code for the AFNetworking library?
Here is the code I am using for AFnetworking library...
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
_emailAddressField.text, #"email",
_usernameField.text, #"username",
_passwordField.text, #"password",
nil];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:%"http://url.com/api/whatever/"];
[client postPath:#"/" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *text = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", text);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#", [error localizedDescription]);
}];
If you're using AFNetworking, you can use multipartFormRequestWithMethod to upload an image:
// Create the http client
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseUrl:url];
// Set parameters
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys: #"param1", #"key1", nil];
// Create the request with the image data and file name, mime type, etc.
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:method path:#"url/to/" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:nameData fileName:fileName mimeType:mimeType];
}];
And then you can add the upload progress block to get feedback of the upload process:
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
//Manage upload percentage
}];
Also, you can add setCompletionBlockWithSuccess to catch success and failure in your operation. More info can be found here. At last but not least important, add the request to the operation queue:
[httpClient enqueueHTTPRequestOperation:operation];

Pass response from AFHTTPRequestOperation to UIWebview

My code is making two requests to the server. One into the uiwebview directly and one with the AFHTTPRequestOperation. I'd like to use the AFHTTPRequestOperation and just pass the response into my uiwebview. What is the correct syntax for passing the response into the uiwebview and not loading it again? How do I do that without calling twice from the server? I still want to be able to test the success or failure of the load request and also send username and password to connect to the url.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[itemField resignFirstResponder];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *userName = [defaults objectForKey:#"storedUserName"];
NSString *passWord = [defaults objectForKey:#"storedPassWord"];
NSURL *url = [NSURL URLWithString:#"http://www.example.net/"];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL: url];
[client setAuthorizationHeaderWithUsername:userName password:passWord];
NSMutableURLRequest *request = [client requestWithMethod:#"GET" path:[#"/example/itemlookup.php?item=" stringByAppendingString:itemField.text] parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//calling again here was the only way I could figure out to get this into my webview
//need to get response from above and put into the uiwebview
[webView loadRequest:request];
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
return YES;
}
I'd recommend using AFNetworking's UIWebView category which does exactly this; uses an AFHTTPRequestOperation to load the content of a webpage and then loads it into the web view as an HTML string.
Otherwise I'd recommend taking a look at the category to see if you can adapt its code for your use. https://github.com/AFNetworking/AFNetworking/blob/master/UIKit%2BAFNetworking/UIWebView%2BAFNetworking.m
If that URL is returning HTML, you can use the - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL method of the UIWebView.
Something like:
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[webView loadHTMLString:responseObject baseURL:url]; //If responseObject is HTML
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
The only thing that might change is the response object. You may be not getting a string, but a NSURLResponse or something to that nature.