Facebook iOS SDK Graph Api Public Page Feed - ios

Can anyone point me in the direction of a thorough tutorial on how to get a public page's post feed? I'd like to really understand what's going on rather than copying and pasting. Even still I haven't found anything I could copy and paste that works. I've managed to make some headway with the following code but the resulting json is not pretty and making extracting image urls virtually impossible (it doesn't require an appId, which I do actually have entered into the plist anyway). I'd rather be using FBRequestConnection. It doesn't seem like it'd be too challenging but I'm unfamiliar with the sdk and the documentation on this seems a bit wanting.
NSString *pageId = #"##################";
NSString *urlString = [NSString stringWithFormat:#"https://www.facebook.com/feeds/page.php?format=json&id=%#", pageId];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.tags = [[NSArray alloc] initWithObjects:#"#meditation", nil];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
QuoteItem *menuItem = [[QuoteItem alloc] init];
if (data.length > 0 && connectionError == nil)
{
NSDictionary *fbFeed = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
NSMutableArray *entries = [fbFeed objectForKey:#"entries"];
I was trying to keep it brief, but to further clarify I've tried using the instruction given by facebook here (https://developers.facebook.com/docs/graph-api/reference/v2.2/page/feed#read) by doing the following:
[FBRequestConnection startWithGraphPath:#"/###########9055/feed"
parameters:nil
HTTPMethod:#"GET"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
/* handle the result
}];
And I get this runtime error: "FBSDKLog: Error for request to endpoint '/164313873599055/feed': An open FBSession must be specified for calls to this endpoint." So I looked up FBSession (which is not mentioned at the destination of the aforementioned link) and tried to remedy the situation but I'm not sure how to set permissions. What I have a tried led to a prompt for the user to allow access to their feed, which is not wanted. I just want the user to see this darn public page feed without having to log in, without seeing anything facebook related in the app.
Which is why I was looking for a thorough tutorial to step a person through the above in one place. These tutorials, I know, do exist for other aspects of iOS development and I was wondering if there was something similar for this case WITHOUT having to write an essay.

I doubt that you had a look in the Facebook docs, because there it's all outlined:
https://developers.facebook.com/docs/graph-api/reference/v2.2/page/feed/#read
The call is
/{page_id}/feed
where {page_id} is the id of the Page you're looking for. You'll need to add an Access Token to the call.

Related

Where is an ideal location to store the root url in an iOS app to get access to an external API?

Where is an ideal location to store the root url in an iOS app to get access to an external API?
I'm thinking of using NSBundle but not sure if this is the right. Or should I be using a constant variable?
Thanks in advance!
You have a few options:
Create a static class that is built to just interact with the API and keep the URL there.
You can use a constant variable defined in one of your headers and import it
You can use a constant variable defined in Prefix.pch (I don't recommend this)
When I build production grade apps I ALWAYS use the 1st one. It takes a little bit longer, but cleans up code so much more. Let's say that I'm interacting with an API that has my Todo list on a server somewhere. I want to request my first Todo list item where each Todo list item is a Title and Content.
I'll first create a Todo list class that has two properties and one method:
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *content;
+ (MyTodoListItem*)todoListItemFromDictionary:(NSDictionary*)dict;
All that one method will do is convert my dictionary that I create in my networking class from a JSON response to a TodoList item.
Now my networking class will have a method definition like:
+ (void)getFirstTodoListItemWithCallback:(void(^)(MyTodoListItem *item))callback;
And the implementation will have all of this in it:
static NSString * const apiRootURL = #"http://api.foo.com/v1/";
+ (void)getFirstTodoListItemWithCallback:(void(^)(MyTodoListItem *item))callback {
NSURL *url = [NSURL URLWithString:urlString];
//where I use the apiRootURL
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL urlWithString:[NSString stringWithFormat:#"%#%#", apiRootURL, #"firstTodoListItem"]]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"Error,%#", [error localizedDescription]);
} else {
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
//where the magic happens so that I never deal with JSON anywhere else.
callback([MyTodoListItem todoListItemFromDictionary:dictionary]);
}
}];
}
And finally, to do something with that object:
- (void)getMyFirstObjectSomewhereInSomeViewController {
[MyTodoListNetworker getFirstTodoListItemWithCallback:(void(^)(MyTodoListItem *item))callback {
NSLog(#"%# %#", item.title, item.content);
}];
}
If you want to change it at runtime you can use the settings bundle. The settings system is a good place to store stuff like this.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html
If you don't want the user to be able to modify the url, you can add plist for the urls you are using, or just add a key into the Info.plist. Many third-party APIs want keys added to the Info.plist and it's a good place to store immutable though not-sensitive data. If the url has sensitive information in it, you should never store it in the main bundle in any form.

FB Graph Subscription IOS send access_token

There is something to the Facebook Graph SDK (and its iOS implementation) that really makes my life miserable. And it seems I'm on my own, because I don't seem to be able to find anyone on the 'net with a similar problem/lack of understanding like me.
Without further ado:
I have this code in my iOS app, that is supposed to subscribe my app to user updates:
-(void)subscribeToFacebook{
(FBSession.activeSession.isOpen) ? NSLog(#"is open") : NSLog(#"is closed");
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"page", #"object",
#"http://www.example.com/fbcallback.php", #"callback_url",
#"about, picture", #"fields",
#"ItsMeAlright", #"verify_token",
nil
];
/* make the API call */
[FBRequestConnection startWithGraphPath:#"/app/subscriptions"
parameters:params
HTTPMethod:#"POST"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
/* handle the result */
NSLog(#"error= %#",error);
}];
}
When I run this code, an error is logged, stating:
"(#15) This method must be called with an app access_token."
Now, I (think) I know how to get the access_token
NSString *fbAccessToken = [[[FBSession activeSession] accessTokenData] accessToken];
but I have't got a clue on where and when to send it to Facebook.
I tried to append it to the #"/app/subscriptions" part like this
NSString *urlString = [NSString
stringWithFormat:#"/app/subscriptions?access_token=%#",
[fbAccessToken stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
but that didn't work.
Your input is extremely welcome!
The way to do this will be to create a FBSession with the application token. Because the current session isn't what you want.
https://developers.facebook.com/docs/reference/ios/current/class/FBSessionTokenCachingStrategy
https://developers.facebook.com/docs/facebook-login/ios#sessions
But, security wise, you shouldn't be doing this at all. It's very insecure to embed your application token in a production application. I believe that the iOS sample generated for you is incorrect.
You should be sending any request that requires the application token with a server call (e.g. PHP)

Force app to wait for method completion (data download)

I'm working with an app that requests data from an OAuth2.0 protected server. When I use the GTM OAuth Library to retrieve data, the program continues to run while the data is being downloaded in the background. I need some sort of mechanism to either force my application to wait until the didFinishWithData selector is called,or I need a way to notify my ViewController of the download's completion, so I can then utilize the data immediately.
I've tried conditional blocks, but those aren't doing it for me. I've also tried polling the object whose data I'm interested in, but if I do that, the data never seems to download. I've heard I can somehow utilize the Notification Center to accomplish this task, so I'll look more into that while I'm waiting for replies here.
Here is basically what is going on:
-(void) getAlert{
// Define the URL of the API module we'd like to utilize.
NSURL *url = [[NSURL alloc] initWithString:#"https://access.active911.com/interface/open_api/api/alerts"];
// Constructs a an HTTP request object to send to the server in order to obtain data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:#"1" forHTTPHeaderField:#"alert_days"];
// This fetcher sends the request along with the authentication header in a recognizable manner.
GTMHTTPFetcher *fetcher = [[GTMHTTPFetcher alloc] initWithRequest:request];
// Attach the OAuth credentials for the fetcher's use.
[fetcher setAuthorizer:auth];
// Execute the operation.
[fetcher waitForCompletionWithTimeout:10];
NSLog(#"About to get alert");
[fetcher beginFetchWithDelegate:self didFinishSelector:#selector(responseHandler:finishedWithData:finishedWithError:)];
NSLog(#"got alert");
}
-(void)responseHandler:(id)valueNotUsed finishedWithData:(NSData *)data finishedWithError:(NSError *)error{
// Retrieve the server data in a usable object
// All that's being done here is conversion to an NSDictionary
// followed by the creation of subdictionaries from that dictionary
// until our final value can be picked directly out of the resulting dict
NSData *jsonData = [[NSData alloc] initWithData:data];
NSError *dictError;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:jsonData //1
options:kNilOptions
error:&dictError];
NSDictionary *token = [json objectForKeyedSubscript:#"message"];
NSArray *alerts = [token objectForKeyedSubscript:#"alerts"];
NSDictionary *alertData = alerts[0];
mapCode = [alertData objectForKeyedSubscript:#"map_code"];
NSString *city = [alertData objectForKeyedSubscript:#"city"];
NSLog(#"Map code: '%#' with city '%#' and access token %#", mapCode, city, accessToken);
}
And I need to pass the mapCode to my view controller.
Thanks for the help!
First off, please rethink about having the UI halt while you fetch results from the server. This can create an extremely bad UX for the app and only should be done if absolutely necessary.
Second, does your responseHandler method work? And do you only need mapCode in the VC that responseHandler is in?
If so, you don't even need to use Notifications. Simply do:
-(void)responseHandler:(id)valueNotUsed finishedWithData:(NSData *)data finishedWithError:(NSError *)error{
...
...
mapCode = [alertData objectForKeyedSubscript:#"map_code"];
[self updateVCWithMapCode:mapCode];
}
That will call the method after the response has been received. Passing it explicitly too so you don't need to have mapCode be a property as well.

WHAT ARE BEST PRACTICE USING NSURLCONNECTION AND NSXMLPARSER?

This question is regarding the best way to implement connections and parsers in my app.
For Instance, my application contains 15 URL request which can return 15 expected return XML format responses.
Usually I Follow the following method.
1. URLManager //This file manages URL and request paths
2. MyConnection//In this Class, I make NSURLConnection and one delegate to return back to Controller when opertion is done. I distinguish URLs using enum.
3. Several Parsers for parsing NSData which my controller gets from Connection file. as parsing finished, I use Delegate to pass Parsed Data to Controller which is usually objects in an array to use in Controller.
By this way I need to make many parsers and also had to face stuck GUI for a whiile until values gets downloaded and parsed.
I want to know the standard way to develop an iOS application so that it could give best performance and stability.
Thanks
According to me.
The Best way is to use sendAsynchronousRequest with block coding
enum for the webservice respoce.
typedef void (^onGetSuccess)(BOOL success,NSArray *arrProduct);
**Webservice Calling**
-(void)getData:(onGetSuccess)block{
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:GET_SERVERT_URL]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if ([data length]>0) {
NSString *str=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *dict=[NSDictionary dictionaryWithXMLString:str];
NSMutableArray *arrRespoce=[dict objectForKey:#"dict"];
block(YES,arrRespoce);
}
else{
block(false,nil);
}
}];
}
For the xml to dict convertion
XMLDictionary

Google Places API Autocomplete on iOS

Over the last few days I've been struggling to get Google Places autocomplete to work on my app, and no matter what I do, I always get the REQUEST_DENIED error.
I followed Google's "tutorial" for the implementation, Places API is enabled, API key has the correct bundle ID, I also tested with a Browser ID, with no success.
I am pretty sure it has something to do with the key, though. Curiously, on the Android version of the app, the service will work with the Browser Key. And on the browser, obviously, it works with that key too.
These are the two keys I experimented with:
And this is my implementation code, using AFNetworking:
NSURL *url = [NSURL URLWithString:#"https://maps.googleapis.com/maps/api/place/autocomplete/"];
NSDictionary *params = #{#"input" : [input stringByReplacingOccurrencesOfString:#" " withString:#"+"],
#"location" : [NSString stringWithFormat:#"%f,%f", searchCoordinate.latitude, searchCoordinate.longitude],
#"sensor" : #(true),
// #"language" : #"pt_BR",
// #"types" : #"(regions)",
// #"components" : #"components=country:br",
#"key" : GOOGLE_API_KEY};
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
[httpClient getPath:#"json"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *JSON = [[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil] dictionaryWithoutNulls];
if (completion) {
completion(JSON[#"predictions"]);
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *errorResponse) {
NSLog(#"[HTTPClient Error]: %# for URL %#", [errorResponse localizedDescription], [[[operation request] URL] path]);
}];
I know there are some questions like this one here, but some are old, and say that Places API does not work on Android or iOS, which clearly is not the case anymore, since Google itself publishes examples on both platforms, as seen on Places API page: https://developers.google.com/places/documentation/autocomplete
A workaround I'm currently using is Apple's GeoCoding system, which works good when you type the full address, but is terrible with half-typed phrases. This is not good at all, I'd really like to use Google's API.
I got it!
The paramenter sensor should receive either #"true"or #"false", and not #(true)or #(false).
For the record, the API key used is indeed the Browser Key, and not an iOS key.
You should init httpClient with base URL #"https://maps.googleapis.com/" and get path /maps/api/place/autocomplete/json. Base URL - host only, it does not take other parts of path, so in your case you get request to URL "https://maps.googleapis.com/json".
https://maps.googleapis.com/maps/api/geocode/json?address="%#","%#"&sensor=false,yourAddress,your c

Resources