Pass response from AFHTTPRequestOperation to UIWebview - objective-c

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.

Related

Having problems using AFNetworking fetching JSON

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.

AFNetworking JSON Request, neither success nor error block called

I'm trying to learn AFNetworking so have written a simple block. I'm trying to retrieve & log the json from the site url below.
NSString *string =
#"http://transportapi.com/v3/uk/bus/stop/490012745J/live.json?api_key=6ee115459cbeccdb902b14d39b61330d&app_id=9deefeb1&group=route";
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary *mydict = (NSDictionary *)responseObject;
NSString *key;
for(key in mydict){
NSLog(#" key %#", key);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"ERROR");
}];
But neither the success or the failure block is being called. Can someone point out what I've done wrong?
You're not actually firing the operation you need to add
[[NSOperationQueue mainQueue] addOperation:operation];

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);
}];

Asana API error - unsupported browser when using an ios client

I am sending an API call to Asana from an iOS client and am getting an HTML page with the title: Asana - Unsupported Browser which tells me something is wrong with the way I make the call.
My API calling code is as follows:
Client Settings are here:
- (id) initHTTPClient {
self = [super initWithBaseURL:[NSURL URLWithString:ASANA_BASE_URL]];
if (self) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
[self setParameterEncoding:AFJSONParameterEncoding];
[self setAuthorizationHeaderWithUsername:ASANA_API_KEY password:[NSString string]];
}
return self;
}
API call is constructed here:
- (void) getCurrentUser {
NSDictionary *parameters = [NSDictionary dictionary];
NSMutableURLRequest *request = [self requestWithMethod:#"GET" path:#"/users/me.json" parameters:parameters];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError *error;
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error];
NSLog(#"Success, here's what we got: %#",response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure, error: %#",error.debugDescription);
}];
[operation start];
}