How to Move to the Next Page in Facebook JSON Response Using iOS Sdk

How to move to the next page in Facebook JSON response using iOS SDK?

all what you need that add a method to the Facebook subclass or itself

- (void)requestWithURLString:(NSString *)fullURL
andHttpMethod:(NSString *)httpMethod
andDelegate:(id <FBRequestDelegate>)delegate {
[self openUrl:fullURL params:nil httpMethod:httpMethod delegate:delegate];
}

ps the second param "httpMethod" may be always @"GET" you can omit it

iOS Facebook Graph API Use next or previous Url for Pagination Using SDK

So in my searching for an obvious answer I stumbled upon the Facebook iOS SDK source on github.com and found this class: https://github.com/facebook/facebook-ios-sdk/blob/master/src/Network/FBGraphObjectPagingLoader.m.

Within the "- (void)followNextLink" method I found my solution:

 FBRequest *request = [[FBRequest alloc] initWithSession:self.session
graphPath:nil];

FBRequestConnection *connection = [[FBRequestConnection alloc] init];
[connection addRequest:request completionHandler:
^(FBRequestConnection *innerConnection, id result, NSError *error) {
_isResultFromCache = _isResultFromCache || innerConnection.isResultFromCache;
[innerConnection retain];
self.connection = nil;
[self requestCompleted:innerConnection result:result error:error];
[innerConnection release];
}];

// Override the URL using the one passed back in 'next'.
NSURL *url = [NSURL URLWithString:self.nextLink];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
connection.urlRequest = urlRequest;

self.nextLink = nil;

self.connection = connection;
[self.connection startWithCacheIdentity:self.cacheIdentity
skipRoundtripIfCached:self.skipRoundtripIfCached];

The above has/had a lot of code I didn't need so I was able to (with the help of this SO OP) condense it down to:

/* make the API call */
FBRequest *request = [[FBRequest alloc] initWithSession:FBSession.activeSession graphPath:nil];
FBRequestConnection *connection = [[FBRequestConnection alloc] init];
[connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithDictionary:@{@"friends": [result objectForKey:@"data"], @"paging": [result objectForKey:@"paging"]}];
NSLog(@"%@", dictionary);
block(dictionary, error);
}];

// Override the URL using the one passed back in 'next|previous'.
NSURL *url = [NSURL URLWithString:paginationUrl];
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
connection.urlRequest = urlRequest;

[connection start];

In order to assist others who may be needing a more generic approach, I've compiled much of my Facebook API graph calls into a gist found @ https://gist.github.com/tamitutor/c65c262d8343d433cf7f.

Swift version of Facebook iOS SDK and how to access data in Response

I faced this problem today as well. I got the user id and name inside MyProfileRequest

struct Response: GraphResponseProtocol {
init(rawResponse: Any?) {
// Decode JSON from rawResponse into other properties here.
guard let response = rawResponse as? Dictionary<String, Any> else {
return
}

if let name = response["name"],
let id = response["id"] {

print(name)
print(id)
}
}
}

EDIT: I redesigned my code like this to use the values in .success(let response) case

struct Response: GraphResponseProtocol {

var name: String?
var id: String?
var gender: String?
var email: String?
var profilePictureUrl: String?

init(rawResponse: Any?) {
// Decode JSON from rawResponse into other properties here.
guard let response = rawResponse as? Dictionary<String, Any> else {
return
}

if let name = response["name"] as? String {
self.name = name
}

if let id = response["id"] as? String {
self.id = id
}

if let gender = response["gender"] as? String {
self.gender = gender
}

if let email = response["email"] as? String {
self.email = email
}

if let picture = response["picture"] as? Dictionary<String, Any> {

if let data = picture["data"] as? Dictionary<String, Any> {
if let url = data["url"] as? String {
self.profilePictureUrl = url
}
}
}
}
}

And in the success case you can get the values like this:

let connection = GraphRequestConnection()
connection.add(MyProfileRequest()) { response, result in
switch result {
case .success(let response):
print("My facebook id is \(response.id!)") //Make sure to safely unwrap these :)
print("My name is \(response.name!)")
case .failed(let error):
print("Custom Graph Request Failed: \(error)")
}
}
connection.start()

How to retrieve Facebook response using Facebook iOS SDK

I thought may be I should make it a wiki and tell the people how I am doing it. because lot of people are facing similar problem.

The first thing that I did was.

In Facebook.m class I added the following statement in the following method

(void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
safariAuth:(BOOL)trySafariAuth
trySafariAuth = NO;

This prevents a safari page to get open for the facebook login, but it pops up a screen in app itself. Then i created a helper class for Facebook, the header file code is here.

#import <UIKit/UIKit.h>
#import "FBConnect.h"

@interface FaceBookHelper : UIViewController
<FBRequestDelegate,
FBDialogDelegate,
FBSessionDelegate>{

Facebook *facebook;
NSArray *permissions;
}

@property(readonly) Facebook *facebook;

- (void)login;

-(void)getUserInfo:(id)sender;

- (void)getUserFriendList:(id)sender;

-(void)postToFriendsWall;

The .m file.

static NSString* kAppId = @"xxx";
#define ACCESS_TOKEN_KEY @"fb_access_token"
#define EXPIRATION_DATE_KEY @"fb_expiration_date"

@implementation FaceBookHelper

@synthesize facebook;

//////////////////////////////////////////////////////////////////////////////////////////////////
// UIViewController

/**
* initialization
*/
- (id)init {
if (self = [super init]) {
facebook = [[Facebook alloc] initWithAppId:kAppId];
facebook.sessionDelegate = self;
permissions = [[NSArray arrayWithObjects:
@"email", @"read_stream", @"user_birthday",
@"user_about_me", @"publish_stream", @"offline_access", nil] retain];
[self login];
}
return self;

}

///////////////////////////////////////////////////////////////////////////////////////////////////
// NSObject

- (void)dealloc {
[facebook release];
[permissions release];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// private

/**
* Login.
*/
- (void)login {
// only authorize if the access token isn't valid
// if it *is* valid, no need to authenticate. just move on
if (![facebook isSessionValid]) {
[facebook authorize:permissions delegate:self];
}
}

/**
* This is the place only where you will get the hold on the accessToken
*
**/
- (void)fbDidLogin {
NSLog(@"Did Log In");
NSLog(@"Access Token is %@", facebook.accessToken );
NSLog(@"Expiration Date is %@", facebook.expirationDate );
// Store the value in the NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:facebook.accessToken forKey:ACCESS_TOKEN_KEY];
[defaults setObject:facebook.expirationDate forKey:EXPIRATION_DATE_KEY];
[defaults synchronize];
// This is the best place to login because here we know that user has already logged in
[self getUserInfo:self];
//[self getUserFriendList:self];
//[self postToFriendsWall];
}

- (void)fbDidNotLogin:(BOOL)cancelled {
NSLog(@"Failed to log in");
}

- (void)getUserInfo:(id)sender {
[facebook requestWithGraphPath:@"me" andDelegate:self];
}

- (void)getUserFriendList:(id)sender {
[facebook requestWithGraphPath:@"me/friends" andDelegate:self];
}
////////////////////////////////////////////////////////////////////////////////
// FBRequestDelegate

/**
* Called when the Facebook API request has returned a response. This callback
* gives you access to the raw response. It's called before
* (void)request:(FBRequest *)request didLoad:(id)result,
* which is passed the parsed response object.
*/
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
NSLog(@"Inside didReceiveResponse: received response");
//NSLog(@"Status Code @", [response statusCode]);
NSLog(@"URL @", [response URL]);
}

/**
* Called when a request returns and its response has been parsed into
* an object. The resulting object may be a dictionary, an array, a string,
* or a number, depending on the format of the API response. If you need access
* to the raw response, use:
*
* (void)request:(FBRequest *)request
* didReceiveResponse:(NSURLResponse *)response
*/
- (void)request:(FBRequest *)request didLoad:(id)result {
NSLog(@"Inside didLoad");
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
// When we ask for user infor this will happen.
if ([result isKindOfClass:[NSDictionary class]]){
//NSDictionary *hash = result;
NSLog(@"Birthday: %@", [result objectForKey:@"birthday"]);
NSLog(@"Name: %@", [result objectForKey:@"name"]);
}
if ([result isKindOfClass:[NSData class]])
{
NSLog(@"Profile Picture");
//[profilePicture release];
//profilePicture = [[UIImage alloc] initWithData: result];
}
NSLog(@"request returns %@",result);
//if ([result objectForKey:@"owner"]) {}

};

/**
* Called when an error prevents the Facebook API request from completing
* successfully.
*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
//[self.label setText:[error localizedDescription]];
};

////////////////////////////////////////////////////////////////////////////////
// FBDialogDelegate

/**
* Called when a UIServer Dialog successfully return.
*/
- (void)dialogDidComplete:(FBDialog *)dialog {
//[self.label setText:@"publish successfully"];
}

@end

Facebook graph API: feeds missing in json response

Your approach is correct, I've seen the JSON and yes it's missing the second one, I think it's upload app form adroid, try checking the application settings for this app. Or If your trying to retrieve the wall FQL is a much better way

SELECT post_id, actor_id, target_id, message FROM stream WHERE source_id in (SELECT target_id FROM connection WHERE source_id=<uid> AND is_following=1) AND is_hidden = 0 

Handle pagination of Facebook user photos

This is mostly based on my research carried out using the trial-and-error method as Facebook's documentation wasn't helpful at all. I'm happy to learn of a better method of doing this :)

We can then use the calls from the Graph Explorer in the template code:

NSString *yourCall = @”YourGraphExplorerCall”;

FBRequest *fbRequest = [FBRequest requestWithGraphPath:yourCall parameters:nil HTTPMethod:@"GET"];
[fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

if (!error) {

NSDictionary *jsonResponse = (NSDictionary *) result;
// Do your stuff with the JSON response
}
else {

failureHandler(error);
}
}];

Facebook Graph accepts and replies in JSON.

Obtaining a user’s albums and photos - the problem of pagination

Once a user is logged in their session is handled behind the scenes by the Facebook API so there’s no need to be concerned about that, we can just perform specific requests we want.

To get the albums data for a user, put this string into the Graph Explorer:

me?fields=albums.fields(count,id)

This’ll ask Fb for the count of photos in each album and their IDs. Notice that the 1st level of the JSON reply contains the ID of the user as well as the “albums” array which contains the “data” array - that’s the array of the actual albums we’re interested in.

Having the IDs of each album we can explore their photos. The below call will get links to each album’s source photo and its miniature:

<album_id>?fields=photos.fields(source,picture)

where is the actual ID of the album whose photos you want to get.

The initial problem is that since there might be many photos in an album, trying to get them in one go is probably a bad idea - that’s why Facebook devs introduced pagination into these calls. What this means is that you can set a limit for the number of photos data you get in a single call and you then use a “cursor” for the next/previous batches that you want to get, with said cursors being given to you in each call.
The main problem is dealing with such paginated data. If we look at the data returned in our previous call we can see that there’s a “paging” section with “cursors” (which contains “before” and “after”) and “next”.
The “next” key is a link which looks very similar to our call string used in the Graph Explorer - and it ends with the “after” cursor; we could think, then, that it’s possible to simply append the “after” cursor to our call string

<album_id>?fields=photos.fields(source,picture)&after=<after_cursor>

and feed that into the Graph Explorer. Nope! For some reason this won’t work as expected - it still directs us to the first batch instead of the next one.
However, the “next” link still works so it’s possible to use parts of it instead of the call we made to the Graph Explorer. Thus the call to get the photos:

<album_id>?fields=photos.fields(source,picture)

becomes:

<album_id>/photos?fields=source%2Cpicture&limit=25

Moreover, it still works after being appended with &after=:

<album_id>/photos?fields=source%2Cpicture&limit=25&after=

Thus it’s easy to simply get the value of “next” in each call for a batch and append it to the above string for the next call.

Here’s the snippet of the final version of the code:

NSString *const FACEBOOK_GRAPH_LIST_ALBUMS = @"me?fields=albums.fields(count,id,name)";
NSString *const FACEBOOK_GRAPH_LIST_ALBUM_PHOTOS = @"/photos?fields=source%2Cpicture&limit=25&after=";
NSArray *currentUsersFacebookAlbums;

- (void) getUserPhotosWithSuccess:(void (^) ())successHandler failure:(void (^) (NSError *error))failureHandler {

FBRequest *fbRequest = [FBRequest requestWithGraphPath:FACEBOOK_GRAPH_LIST_ALBUMS parameters:nil HTTPMethod:@"GET"];
[fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

if (!error) {

NSDictionary *jsonResponse = (NSDictionary *) result;
currentUsersFacebookAlbums = (NSArray *) [[jsonResponse valueForKey:@"albums"] valueForKey:@"data"];

for (NSDictionary *currentAlbum in currentUsersFacebookAlbums) {

NSString *albumId = [currentAlbum valueForKey:@"id"];
[self getCurrentUserFacebookPhotosWithAlbum:albumId afterCursor:nil failure:^(NSError *error) {
failureHandler(error);
}];
}

successHandler();
}
else {

failureHandler(error);
}
}];
}

- (void) getCurrentUserFacebookPhotosWithAlbum:(NSString *) albumId afterCursor:(NSString *) afterCursor failure:(void (^) (NSError *error))failureHandler {

if (afterCursor == nil) {

afterCursor = @"";
}

NSString *fbGraphCall = [NSString stringWithFormat:@"%@%@%@", albumId, FACEBOOK_GRAPH_LIST_ALBUM_PHOTOS, afterCursor];

FBRequest *fbRequest = [FBRequest requestWithGraphPath:fbGraphCall parameters:nil HTTPMethod:@"GET"];
[fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

if (!error) {

NSDictionary *jsonResponse = (NSDictionary *) result;
NSArray *currentPhotoBatch = (NSArray *) [jsonResponse valueForKey:@"data"];

// Go through the currently obtained batch and add them to the returned mutable array
for (NSDictionary *currentPhoto in currentPhotoBatch) {

[[CurrentUserDataHandler sharedInstance] addFacebookPhoto:currentPhoto];
}

// If there's a "next" link in the response, recur the method on the next batch...
if ([[jsonResponse valueForKey:@"paging"] objectForKey:@"next"] != nil) {

// ...by appending the "after" cursor to the call
NSString *afterCursor = [[[jsonResponse valueForKey:@"paging"] valueForKey:@"cursors"] valueForKey:@"after"];
[self getCurrentUserFacebookPhotosWithAlbum:albumId afterCursor:afterCursor failure:^(NSError *error) {
failureHandler(error);
}];
}

if ([[jsonResponse valueForKey:@"paging"] objectForKey:@"next"] != nil && [self isLastAlbum:albumId]) {

[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_FACEBOOK_PHOTOS object:nil];
}
}
else {

failureHandler(error);
}
}];
}

- (bool) isLastAlbum:(NSString *) albumId {

for (NSDictionary *albumData in currentUsersFacebookAlbums) {

if ([albumId isEqualToString:[albumData valueForKey:@"id"]] && [currentUsersFacebookAlbums indexOfObject:albumData] == [currentUsersFacebookAlbums count] - 1) {

return YES;
}
}

return NO;
}

How to retrieve Facebook response using Facebook iOS SDK

I thought may be I should make it a wiki and tell the people how I am doing it. because lot of people are facing similar problem.

The first thing that I did was.

In Facebook.m class I added the following statement in the following method

(void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
safariAuth:(BOOL)trySafariAuth
trySafariAuth = NO;

This prevents a safari page to get open for the facebook login, but it pops up a screen in app itself. Then i created a helper class for Facebook, the header file code is here.

#import <UIKit/UIKit.h>
#import "FBConnect.h"

@interface FaceBookHelper : UIViewController
<FBRequestDelegate,
FBDialogDelegate,
FBSessionDelegate>{

Facebook *facebook;
NSArray *permissions;
}

@property(readonly) Facebook *facebook;

- (void)login;

-(void)getUserInfo:(id)sender;

- (void)getUserFriendList:(id)sender;

-(void)postToFriendsWall;

The .m file.

static NSString* kAppId = @"xxx";
#define ACCESS_TOKEN_KEY @"fb_access_token"
#define EXPIRATION_DATE_KEY @"fb_expiration_date"

@implementation FaceBookHelper

@synthesize facebook;

//////////////////////////////////////////////////////////////////////////////////////////////////
// UIViewController

/**
* initialization
*/
- (id)init {
if (self = [super init]) {
facebook = [[Facebook alloc] initWithAppId:kAppId];
facebook.sessionDelegate = self;
permissions = [[NSArray arrayWithObjects:
@"email", @"read_stream", @"user_birthday",
@"user_about_me", @"publish_stream", @"offline_access", nil] retain];
[self login];
}
return self;

}

///////////////////////////////////////////////////////////////////////////////////////////////////
// NSObject

- (void)dealloc {
[facebook release];
[permissions release];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// private

/**
* Login.
*/
- (void)login {
// only authorize if the access token isn't valid
// if it *is* valid, no need to authenticate. just move on
if (![facebook isSessionValid]) {
[facebook authorize:permissions delegate:self];
}
}

/**
* This is the place only where you will get the hold on the accessToken
*
**/
- (void)fbDidLogin {
NSLog(@"Did Log In");
NSLog(@"Access Token is %@", facebook.accessToken );
NSLog(@"Expiration Date is %@", facebook.expirationDate );
// Store the value in the NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:facebook.accessToken forKey:ACCESS_TOKEN_KEY];
[defaults setObject:facebook.expirationDate forKey:EXPIRATION_DATE_KEY];
[defaults synchronize];
// This is the best place to login because here we know that user has already logged in
[self getUserInfo:self];
//[self getUserFriendList:self];
//[self postToFriendsWall];
}

- (void)fbDidNotLogin:(BOOL)cancelled {
NSLog(@"Failed to log in");
}

- (void)getUserInfo:(id)sender {
[facebook requestWithGraphPath:@"me" andDelegate:self];
}

- (void)getUserFriendList:(id)sender {
[facebook requestWithGraphPath:@"me/friends" andDelegate:self];
}
////////////////////////////////////////////////////////////////////////////////
// FBRequestDelegate

/**
* Called when the Facebook API request has returned a response. This callback
* gives you access to the raw response. It's called before
* (void)request:(FBRequest *)request didLoad:(id)result,
* which is passed the parsed response object.
*/
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
NSLog(@"Inside didReceiveResponse: received response");
//NSLog(@"Status Code @", [response statusCode]);
NSLog(@"URL @", [response URL]);
}

/**
* Called when a request returns and its response has been parsed into
* an object. The resulting object may be a dictionary, an array, a string,
* or a number, depending on the format of the API response. If you need access
* to the raw response, use:
*
* (void)request:(FBRequest *)request
* didReceiveResponse:(NSURLResponse *)response
*/
- (void)request:(FBRequest *)request didLoad:(id)result {
NSLog(@"Inside didLoad");
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
// When we ask for user infor this will happen.
if ([result isKindOfClass:[NSDictionary class]]){
//NSDictionary *hash = result;
NSLog(@"Birthday: %@", [result objectForKey:@"birthday"]);
NSLog(@"Name: %@", [result objectForKey:@"name"]);
}
if ([result isKindOfClass:[NSData class]])
{
NSLog(@"Profile Picture");
//[profilePicture release];
//profilePicture = [[UIImage alloc] initWithData: result];
}
NSLog(@"request returns %@",result);
//if ([result objectForKey:@"owner"]) {}

};

/**
* Called when an error prevents the Facebook API request from completing
* successfully.
*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
//[self.label setText:[error localizedDescription]];
};

////////////////////////////////////////////////////////////////////////////////
// FBDialogDelegate

/**
* Called when a UIServer Dialog successfully return.
*/
- (void)dialogDidComplete:(FBDialog *)dialog {
//[self.label setText:@"publish successfully"];
}

@end

Facebook connect graph status objects have comments capped at 25

This is just the way the Graph API works. Take a look at the API docs. You get 25 at a time and have to loop through them. You can use the timestamp (created_time) of the last comment in the batch as a parameter in the next Graph API call or you can use the offset parameter. Which is what I've been doing. I was running into some screwiness using created_time. This is an example from my C# test app. Ignore the references to the PostComment object that's just a data structure I created to hold the data I'm pulling. The magic (and the process i'm referencing) is in the parameters being passed to the graph API call:

parameters.Add("offset", numPostComments);
parameters.Add("limit", 25);

I'm fairly certain you can set the "limit" to anything 25 or below.

do
{
foreach (var comment in comments.data)
{
numPostComments++;
PostComment pc = new PostComment();
pc.Post_ID = p.Id;
pc.Facebook_ID = comment.id;
pc.From = comment.from.name;
if (comment.likes != null)
pc.Likes = (int)comment.likes;
pc.CommentDate = DateTime.Parse(comment.created_time);
pc.CommentText = comment.message;
p.Comments.Add(pc);
}
// Create new Parameters object for call to API
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("offset", numPostComments);
parameters.Add("limit", 25);

// Call the API to get the next block of 25
comments = client.Get(string.Format("{0}/comments", p.Facebook_ID), parameters);
} while (comments.data.Count > 0);


Related Topics



Leave a reply



Submit