Retrieving website via API call from site and display to user - objective-c

I am developing an iphone app.
The first screen of my app shows a list of services available for the user.
The user than selects one of the services and goes to the next screen where they can order that service.
I need to retrieve the list of services using an api call from my website.
What is the the way to do so in IOS without affecting the user experience? I don't want the user to be waiting for me to retrieve the list of services from my site every time they access that page.
I am using the AFNetworking API to get the list of services now.
Thanks!

AFNetworking sounds good.
Make sure you use it asynchronous and with a completion block
e.g.
NSMutableURLRequest *urlRequest = ...
AFHTTPRequestOperation *request = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
[request setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
... handle your services
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
... handle error
}];
[request start];
if the request is JSON, use the AFJSONRequest class

Related

Afnetworking 2.0 PUT image

I am able to use put method of afnetworking 2.0 successfully for putting data.
NSString* PUTURL = [NSString
stringWithFormat:#"%#/updateestado/estado/%#/idJugador/%ld",BASEURl,[status
urlEncodeUsingEncoding:NSUTF8StringEncoding],userId];
NSLog(#"REG URL----%#",PUTURL);
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager PUT:PUTURL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[self.NET_Delegate DelegateUpdateStatusResponce:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.NET_Delegate DelegateUpdateStatusError:error];
}];
I am able to upload video successfully using post method.
But The requirement for uploading image is using PUT
I followed the way given in
Simple "PUT" file upload with AFNetworking
but had two issues says multipartFormRequestWithMethod deprived &
when trying the approach get 404 error.
No reference for this on afnetworking doc in github.
Query: I am working on uploading image using put first time, so i think i am missing some thing. Any reference or code samples to achieve this will be helpful. Thanks
You are not uploading an image; but rather a URL of the image (note the url parameter).
Therefore you will need to upload the image to a 3rd party site and then post the link to whatever that service is.
It's impossible to upload an image using a PUT request so you must be missing something.

Check if Authenticated User Likes A Certain Page (Using SLRequest)?

I've been looking through the open grpah reference docs, and can't seem to find a good example related to SLRequest regarding users and whether they like a certain page or not. I don't want to iterate through their entire list of likes.
I was looking at https://developers.facebook.com/docs/graph-api/reference/user/likes/ but I'm not sure how to work with it and SLRequest, I've done the following but I'm receiving the following response:
Received Response: {"error":{"message":"An active access token must be used to query
information about the current
user.","type":"OAuthException","code":2500}}
Thing is all of this code only runs if the suer has previously authenticated and granted permission, and so it must have found an account, yet I still get this respond. Could it be that I'm not requesting a specific permission? I've requested, "email, public_actions, user_likes and public_stream".
BTW I do have another view controller where I ask for these permissions (except user_likes), and everything works in that one. Is there anything that I'm missing here? Thanks!
NSURL *feedURL = [NSURL URLWithString:#"https://graph.facebook.com/me/likes/{MYPAGEID}"];
[SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:feedURL parameters:nil];
[updateFeedRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (responseData ) {
NSLog(#"Received Response: %#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
if (urlResponse.statusCode >= 200 && urlResponse.statusCode < 300) {
NSLog(#"Successful Connection");
} else {
}
} else {
NSLog(#"No response.");
}
}];
The error that you are getting is received when you try to make calls like /me but no user is currently logged-in to your app or the user has already logged-out of the app.
I'm not sure of your complete flow of the app but you can try this to validate your access token and call- you have the access token right? so, with your current API call just add an extra parameter access_token with its value.

Got response data from AFNetworking - now what? (Login/Signup on website via app)

I want to make a login/signup feature for my iphone program, and I decided to use AFNetworking for it. First I send POST request to the website I want to login at, and then get response data from it.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *parameters = #{#"username": #"myusername", #"password": #"mypassword"};
[manager POST:#"http://website.com/login" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
Data *data = (NSData*)responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
What should I do next in order to authorize myself on the website and get access to the information of the logged in user. The responseObject in my code contains just the html for the login webpage.
Any information would be strongly appreciated!
Thank you in advance!
With this approach i strictly suggest you to use another server module for login. First of all, you could create a sort of Api call method reachable from a client ( eg: /api/login.php ), so passing alle necessaries params. Use https, and/or encrypt username and password with a standard method such as AES-256 or you own custom method (you can also see the OAuth protocol for secure authentication between client and server).
Another way is to use a UIWebView ( for iOS ) or WebView ( for Mac OS ), so calling your webpage you are logged in, it depends from which what you want to do.

AFNetworking: interrupt background requests for higher priority requests

Using AFNetworking, I need to download ~100 images in the background and store them to disk while ensuring any other network connectivity in my app takes precedence.
~~~~~~~
I've got an application that has 4 tabs. Each tab basically does the same thing: Pulls down a JSON response from a server, and displays a thumbnail grid of images, pulling down each image on demand (using AF's ImageView category). Tapping on a thumbnail takes you to a detail view controller where you see a larger image. The response and images are different for each tab.
There's a new requirement to fetch all of the images for the 4th tab ahead of time, so that theoretically by the time the user taps on the 4th tab, the JSON data and images are being read from disk.
I've got this working more or less right now, with the 4th tab pre-fetching and saving to disk being performed on a background thread so the main thread doesn't lock up. However, the network requests being kicked off when a user is on the 1st, 2nd or 3rd tab are being blocked by the pre-fetching network requests.
I'm using AFNetworking, and here is the code I'm using when Tab 1, 2 or 3 loads:
// this network request ends up getting blocked by the network request that
// is fired upon the application becoming active
- (void)getAllObjectDataWithBlock:(AFCompletionBlockWrapper)block
{
[[[MyAPIClient] sharedClient] getPath:#"" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
block(operation, responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
block(operation, nil, error);
}];
}
And here is the code I'm using when my application becomes active, to pre-fetch content for the 4th tab:
// this network request runs in the background, but still blocks requests
// that should have a higher priority
- (void)applicationDidBecomeActive:(UIApplication *)application
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSOperationQueue *imageQueue = [[NSOperationQueue alloc] init];
[imageQueue setMaxConcurrentOperationCount:8];
for (NSString *imageURL in self.images) {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL imageURL]];
AFImageRequestOperation *operation = [[AFImageRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"fail");
}];
[imageQueue addOperation:smallOperation];
}
});
}
How can I structure things so any network requests kicked off from the main thread interrupt those that were kicked off in the background thread?
I don't know that you can easily interrupt running operations, unless you want to send them a cancel--but you'd have to look at whether AFImageRequestOperation pays attention to isCancelled.
Have you tried using setQueuePriority? You could start all the pre-fetching requests with a low priority, then add current tab requests with a higher priority. I believe running operations would complete, but once they complete, your higher-priority operations would get scheduled ahead of queued low-priority ones.

Google App Engine with ClientLogin Interface for Objective-C

I'm experiencing the same problem in this previous stackoverflow.com post.
Specifically, I seem to be able to get the "Auth" token correctly, but attempts to use it in the header when I access later pages still just return me the login page's HTML.
Following links related to this post, I've determined that you need to make a subsequent call to this URL.
A call to the URL will then give you an ACSID cookie which then needs to be passed in subsequent calls in order to maintain an authenticated state.
When requesting this cookie, I've read various posts saying you need to specify your original auth token by appending it to the query string such that:
?auth=this_is_my_token
I've also read that you should set it in the http header as described in google's documentation such that a http header name/value is:
Authorization: GoogleLogin auth=yourAuthToken
I've tried both approaches and am not seeing any cookies returned. I've used Wireshark, LiveHttpHeaders for Firefox, and simple NSLog statements trying to see if anything like this is returned.
Below is the code snippet I've been using.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://yourapp.appspot.com/_ah/login?auth=%#", [token objectForKey:#"Auth"]]];
NSHTTPURLResponse* response;
NSError* error;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:[NSString stringWithFormat:#"GoogleLogin auth=%#", [token objectForKey:#"Auth"]] forHTTPHeaderField:#"Authorization"];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//show me all header fields
NSLog([[response allHeaderFields] description]);
//show me the response
NSLog(#"%#", [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#"http://yourapp.appspot.com/_ah/login"]];
//show me all cookies
for (NSHTTPCookie *cookie in all)
{
NSLog(#"Name: %# : Value: %#", cookie.name, cookie.value);
}
I hope you can use ClientLogin for Google App Engine code.
Adding sample code to this question because someone contacted me directly about my solution. Note that you must set the "service" parameter equal to "ah" on the initial token request.
Initial Request of Token [done synchronously] NOTE: the "service" parameter is set to "ah" and the "source" is just set to "myapp", you should use your app name.
//create request
NSString* content = [NSString stringWithFormat:#"accountType=HOSTED_OR_GOOGLE&Email=%#&Passwd=%#&service=ah&source=myapp", [loginView username].text, [loginView password].text];
NSURL* authUrl = [NSURL URLWithString:#"https://www.google.com/accounts/ClientLogin"];
NSMutableURLRequest* authRequest = [[NSMutableURLRequest alloc] initWithURL:authUrl];
[authRequest setHTTPMethod:#"POST"];
[authRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-type"];
[authRequest setHTTPBody:[content dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* authResponse;
NSError* authError;
NSData * authData = [NSURLConnection sendSynchronousRequest:authRequest returningResponse:&authResponse error:&authError];
NSString *authResponseBody = [[NSString alloc] initWithData:authData encoding:NSASCIIStringEncoding];
//loop through response body which is key=value pairs, seperated by \n. The code below is not optimal and certainly error prone.
NSArray *lines = [authResponseBody componentsSeparatedByString:#"\n"];
NSMutableDictionary* token = [NSMutableDictionary dictionary];
for (NSString* s in lines) {
NSArray* kvpair = [s componentsSeparatedByString:#"="];
if ([kvpair count]>1)
[token setObject:[kvpair objectAtIndex:1] forKey:[kvpair objectAtIndex:0]];
}
//if google returned an error in the body [google returns Error=Bad Authentication in the body. which is weird, not sure if they use status codes]
if ([token objectForKey:#"Error"]) {
//handle error
};
The next step is to get your app running on google app engine to give you the ASCID cookie. I'm not sure why there is this extra step, it seems to be an issue on google's end and probably why GAE is not currently in their listed obj-c google data api library. My tests show I have to request the cookie in order sync with GAE. Also, notice I don't do anything with the cookie. It seems just by requesting it and getting cookied, future requests will automatically contain the cookie. I'm not sure if this is an iphone thing bc my app is an iphone app but I don't fully understand what is happening with this cookie. NOTE: the use of "myapp.appspot.com".
NSURL* cookieUrl = [NSURL URLWithString:[NSString stringWithFormat:#"http://myapp.appspot.com/_ah/login?continue=http://myapp.appspot.com/&auth=%#", [token objectForKey:#"Auth"]]];
NSLog([cookieUrl description]);
NSHTTPURLResponse* cookieResponse;
NSError* cookieError;
NSMutableURLRequest *cookieRequest = [[NSMutableURLRequest alloc] initWithURL:cookieUrl];
[cookieRequest setHTTPMethod:#"GET"];
NSData* cookieData = [NSURLConnection sendSynchronousRequest:cookieRequest returningResponse:&cookieResponse error:&cookieError];
Finally, I can post json to my gae app. NOTE: the snippet below is an async request. We can handle responses by implementing didReceiveResponse, didReceiveData, didFailWIthError.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://myapp.appspot.com/addRun?auth=%#", mytoken]];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:#"my http body";
NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!connectionResponse) {
NSLog(#"Failed to submit request");
} else {
NSLog(#"Request submitted");
}
Check out the code that does this in the official SDK. The latest SDK release even has it split into its own file.
1st - thanks for the great post it really got me started.
2nd - I have been slugging it out with my app, trying to POST to the GAE while authenticated.
This is the request is built when POSTing, once you have acquired the authtoken:
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"image/png" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[request setValue:authtoken forHTTPHeaderField:#"auth"]; // <-- the magic
mattb
I created a few obj-c classes for implementing ClientLogin, including support for Google App Engine:
http://github.com/cameronr/GoogleAppEngineAuth
Note that Google has recently changed the way authorization failure is indicated. They used to place an Error token in the response. Now they just return a 403 (Forbidden) status. This broke my code!
Thank for this post and especially the answer from Keith but it does not works for me.
Even if it seems ok for me ... very strange.
I check this post (How do you access an authenticated Google App Engine service from a (non-web) python client?) which talk about doing the same thing in python. I test it and it works.
And the objective C code proposed by Keith is really similar to the python code.
But when I try to get the "Auth" token authData contains Error=BadAuthentication.
Some one got an idea about possibles problems ?
Using HOSTED_OR_GOOGLE is wrong, and I will explain why.
There are two kinds of accounts in the Google world. The ones you create for GMail, etc are "Google" accounts. The ones you create for Apps for Domains are "Hosted" accounts. You can use a Hosted Account email to make a Google Account, thus creating an email address that is associated with both kinds of accounts.
Your Google App Engine app can be configured to work with (1) Google Accounts or (2) Hosted Accounts for a particular domain.
Assume that we are developing an app for Google Accounts. A user enters in an email address that is associated with a Google Account and a Hosted Account. Google will use their Google Account for the login. This all works fine.
Now, if we use ClientLogin with this same email address and use HOSTED_OR_GOOGLE for the account type, login will be successful, but it will use the Hosted Account, since the Hosted Account takes precedence. As I mentioned above, you cannot use a Hosted Account for an app that expects a Google Account. So the authentication will not work.
So, when using ClientLogin to authenticate with a Google App Engine app, you need to use GOOGLE for the account type if the app is for Google Accounts, or HOSTED for the account type if the app is for a domain.