I am attempting to download Facebook albums of photos from a user in my app. Unfortunately although I do have an access token, I am getting zero albums from the requests. I am not getting an error, just getting zero. Why? If you would like to see any more code or ask more questions, just ask. Note that I have authorized the current user's Facebook permissions when they signed up, and I've since quit the app and opened it many times (don't think this would be an issue, since I have an access token..?)
- (void)getAlbums:(OLFacebookAlbumRequestHandler)handler {
if ([FBSDKAccessToken currentAccessToken]) {
// connection is open, perform the request
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *graphPath = #"me/albums?limit=100&fields=id,name,count,cover_photo";
if (self.after) {
graphPath = [graphPath stringByAppendingFormat:#"&after=%#", self.after];
}
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath parameters:nil];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (self.cancelled) {
return;
}
if (error) {
[OLFacebookAlbumRequest handleFacebookError:error completionHandler:handler];
return;
}
NSString *parsingErrorMessage = #"Failed to parse Facebook Response. Please check your internet connectivity and try again.";
NSError *parsingError = [NSError errorWithDomain:kOLErrorDomainFacebookImagePicker code:kOLErrorCodeFacebookImagePickerBadResponse userInfo:#{NSLocalizedDescriptionKey: parsingErrorMessage}];
id data = [result objectForKey:#"data"];
if (![data isKindOfClass:[NSArray class]]) {
handler(nil, parsingError, nil);
return;
}
NSMutableArray *albums = [[NSMutableArray alloc] init];
for (id album in data) {
if (![album isKindOfClass:[NSDictionary class]]) {
continue;
}
id albumId = [album objectForKey:#"id"];
id photoCount = [album objectForKey:#"count"];
id name = [album objectForKey:#"name"];
if (!([albumId isKindOfClass:[NSString class]] && [photoCount isKindOfClass:[NSNumber class]]
&& [name isKindOfClass:[NSString class]])) {
continue;
}
OLFacebookAlbum *album = [[OLFacebookAlbum alloc] init];
album.albumId = albumId;
album.photoCount = [photoCount unsignedIntegerValue];
album.name = name;
album.coverPhotoURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small&access_token=%#", album.albumId, [FBSDKAccessToken currentAccessToken].tokenString]];
[albums addObject:album];
}
// get next page cursor
OLFacebookAlbumRequest *nextPageRequest = nil;
id paging = [result objectForKey:#"paging"];
if ([paging isKindOfClass:[NSDictionary class]]) {
id cursors = [paging objectForKey:#"cursors"];
id next = [paging objectForKey:#"next"]; // next will be non nil if a next page exists
if (next && [cursors isKindOfClass:[NSDictionary class]]) {
id after = [cursors objectForKey:#"after"];
if ([after isKindOfClass:[NSString class]]) {
nextPageRequest = [[OLFacebookAlbumRequest alloc] init];
nextPageRequest.after = after;
}
}
}
handler(albums, nil, nextPageRequest);
}];
}
else {
NSString *message = #"No Facebook user authentication found.";
handler(nil, [NSError errorWithDomain:kOLErrorDomainFacebookImagePicker code:kOLErrorCodeFacebookImagePickerNoOpenSession userInfo:#{NSLocalizedDescriptionKey: message}], nil);
}
}
//Code for fetching albums...
- (void)loadNextAlbumPage {
self.inProgressRequest = self.albumRequestForNextPage;
self.albumRequestForNextPage = nil;
[self.inProgressRequest getAlbums:^(NSArray/*<OLFacebookAlbum>*/ *albums, NSError *error, OLFacebookAlbumRequest *nextPageRequest) {
self.inProgressRequest = nil;
self.loadingIndicator.hidden = YES;
self.albumRequestForNextPage = nextPageRequest;
if (error) {
if (self.parentViewController.isBeingPresented) {
self.loadingIndicator.hidden = NO;
self.getAlbumError = error; // delay notification so that delegate can dismiss view controller safely if desired.
} else {
[self.delegate albumViewController:self didFailWithError:error];
}
return;
}
NSMutableArray *paths = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < albums.count; ++i) {
[paths addObject:[NSIndexPath indexPathForRow:self.albums.count + i inSection:0]];
}
[self.albums addObjectsFromArray:albums];
if (self.albums.count == albums.count) {
// first insert request
[self.tableView reloadData];
} else {
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
}
if (nextPageRequest) {
self.tableView.tableFooterView = self.loadingFooter;
} else {
self.tableView.tableFooterView = nil;
}
}];
}
//And when they signed up:
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{ #"fields" : #"id,first_name,photos,picture.width(400).height(400)"}]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) { //etc etc the method continues.
FB authentication will give you a unique access_token for a particular set of permissions. To access user photos, you need to request the user_photos permission. Use the FBSDKLoginButton to request permissions.
loginButton.readPermissions = #[#"public_profile", #"email", #"user_photos"];
Once you have an access token with the required permissions, persist that locally (on the device) to reuse in future. If the access code is still valid, you won't need to request it again. If it becomes invalid (in case the user explicitly revoked permissions to your app), send them back to the login screen.
Related
I'm trying to save my VPN configuration to the preferences, which already works (I'm able to connect to my VPN). But for some reason each time i run the code again instead of using the last configuration it creates a new one. So, i end up with a bunch of configurations.
Here is my current code, if anyone could let me know what's going wrong with it that would be awesome. Thanks!
// Initialize Manager
NETunnelProviderManager *manager = [[NETunnelProviderManager alloc] init];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(#"Load Error: %#", error.description);
} else {
// Create the protocol object
NETunnelProviderProtocol *protocol = [[NETunnelProviderProtocol alloc] init]; // Create the protocol object
// Configure the protocol object
protocol.providerBundleIdentifier = #"com.nfisc.testvpn.ptp"; // Bundle ID of tunnel provider
protocol.providerConfiguration = #{}; // Currently blank, but will be used later
protocol.serverAddress = #"0.0.0.0"; // Ommited for security reasons
protocol.username = #"username"; // The username for the configuration
protocol.identityDataPassword = #"password"; // The password for the configuration
protocol.disconnectOnSleep = NO;
// Configure the manager with the protocol
manager.protocolConfiguration = protocol;
manager.enabled = true;
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(#"Save Error: %#", error.description);
} else {
if ([[manager connection] status] != NEVPNStatusConnected) {
NSLog(#"Starting VPN");
[self start:manager];
} else {
NSLog(#"VPN Already Connected");
[_statusLabel setText:#"Connected"];
[_statusLabel setTextColor:[UIColor greenColor]];
}
}
}];
}
}];
Use + (void)loadAllFromPreferencesWithCompletionHandler:(void (^)(NSArray<NEAppProxyProviderManager *> *managers, NSError *error))completionHandler API instead.
create new protocol only when managers.count == 0 in the block.
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if (error) {
NSLog(#"Load Error: %#", error.description);
}
NETunnelProviderManager *manager;
if (managers.count > 0) {
manager = managers[0];
}else {
manager = [[NETunnelProviderManager alloc] init];
manager.protocolConfiguration = [[NETunnelProviderProtocol alloc] init];
}
//... your code here...
}];
The solution was to call [NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler: first and then do the rest.
Fixed Code:
__block NETunnelProviderManager *manager = [[NETunnelProviderManager alloc] init];
NETunnelProviderProtocol *protocol = [[NETunnelProviderProtocol alloc] init];
protocol.providerBundleIdentifier = #"com.nfisc.testvpn.ptp"; // bundle ID of tunnel provider
protocol.providerConfiguration = #{#"key": #"value"};
protocol.serverAddress = #"0.0.0.0"; // VPN server address
protocol.username = #"username";
protocol.identityDataPassword = #"password";
manager.protocolConfiguration = protocol;
manager.enabled = true;
[NETunnelProviderManager loadAllFromPreferencesWithCompletionHandler:^(NSArray<NETunnelProviderManager *> * _Nullable managers, NSError * _Nullable error) {
if ([managers count] > 0) {
manager = [managers objectAtIndex:0];
[self start:manager];
} else {
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(#"Error 1: %#", error.description);
} else {
[manager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(#"Error 2: %#", error.description);
} else {
[self start:manager];
}
}];
}
}];
}
}
I am working on app where I need to import contacts into NSMutableDictionary, but sometimes people not filling all contact details. So leaving just number or company name. Do I need to go through all contacts detail to check what field will be my "display name". In Android I know there is displayName variable. But how it is in Swift or Objective C?
My code:
BOOL success = [addressBook
enumerateContactsWithFetchRequest:request
error:&contactError
usingBlock:^(CNContact *contact, BOOL *stop){
NSString * contactId = contact.identifier;
NSString * firstName = contact.givenName;
NSString * lastName = contact.familyName;
}];
Use CNContactFormatter to build the display name. When specifying the keys for the request, use descriptorForRequiredKeysForStyle to make sure you requested the appropriate fields.
In Swift, it would be:
let store = CNContactStore()
store.requestAccess(for: .contacts) { granted, error in
guard granted else {
print(error?.localizedDescription ?? "Unknown error")
return
}
let request = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)])
let formatter = CNContactFormatter()
formatter.style = .fullName
do {
try store.enumerateContacts(with: request) { contact, stop in
if let name = formatter.string(from: contact) {
print(name)
}
}
} catch let fetchError {
print(fetchError)
}
}
You suggested that you have situations where there is neither name nor company, but just phone number. Well, then, you'd have to manually handle that yourself:
let request = CNContactFetchRequest(keysToFetch: [CNContactIdentifierKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)])
do {
try store.enumerateContacts(with: request) { contact, stop in
if let name = formatter.string(from: contact) {
print(name)
} else if let firstPhone = contact.phoneNumbers.first?.value {
print(firstPhone.stringValue)
} else {
print("no name; no number")
}
}
} catch let fetchError {
print(fetchError)
}
For Swift 2, see previous revision of this answer.
You can fetch contact name from your phone book using this code:-
- (void) fetchContacts
{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusDenied) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:#"This app previously was refused permissions to contacts; Please go to settings and grant permission to this app so it can use contacts" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:TRUE completion:nil];
return;
}
CNContactStore *store = [[CNContactStore alloc] init]; [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
// make sure the user granted us access
if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
// user didn't grant access;
// so, again, tell user here why app needs permissions in order to do it's job;
// this is dispatched to the main queue because this request could be running on background thread
});
return;
}
// build array of contacts
NSMutableArray *contacts = [NSMutableArray array];
NSError *fetchError;
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:#[CNContactIdentifierKey, [CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]]];
BOOL success = [store enumerateContactsWithFetchRequest:request error:&fetchError usingBlock:^(CNContact *contact, BOOL *stop) {
[contacts addObject:contact];
}];
if (!success) {
NSLog(#"error = %#", fetchError);
}
// you can now do something with the list of contacts, for example, to show the names
CNContactFormatter *formatter = [[CNContactFormatter alloc] init];
for (CNContact *contact in contacts) {
if (!_contacts) {
_contacts = [[NSMutableArray alloc] init];
}
NSString *string = [formatter stringFromContact:contact];
NSLog(#"contact = %#", string);
[_contacts addObject:string];
}
[_contactatableview reloadData];
}];
}
#import <Contacts/Contacts.h>
#import <ContactsUI/ContactsUI.h>
- (IBAction)displayContact:(id)sender {
id keysToFetch = #[[CNContactViewController descriptorForRequiredKeys]];
CNContact *contact = [self.store unifiedContactWithIdentifier:self.contactIdentifier keysToFetch:keysToFetch error:nil];
self.controller = [[CNContactViewController alloc] init];
[self.controller.view setFrameSize:NSMakeSize(500, 500)];
[self presentViewController:self.controller asPopoverRelativeToRect:self.view.bounds ofView: self.view preferredEdge: NSMaxXEdge behavior:NSPopoverBehaviorTransient];
self.controller.contact = contact;
}
To fetch contacts from devices
if (isIOS9) { //checking iOS version of Device
CNContactStore *store = [[CNContactStore alloc] init];
//keys with fetching properties
NSArray *keys = #[CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey,CNContactPostalAddressesKey, CNLabelWork, CNLabelDateAnniversary];
NSString *containerId = store.defaultContainerIdentifier;
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:containerId];
NSError *error;
NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
DLOG(#"cnContacts %lu",(unsigned long)cnContacts.count);
if (error) {
//error
} else {
for (CNContact *contact in cnContacts) {
//iterate over cnContacts to get details
}
}
} else {
//for below iOS 9
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef arrPersons = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex count = ABAddressBookGetPersonCount(addressBook);
NSLog(#"cnContacts %lu",(unsigned long)count);
for (int i = 0; i < count; i++) {
ABRecordRef record = CFArrayGetValueAtIndex(arrPersons,i);
//use kABPersonBirthdayProperty to get b’day
NSString *birthDay = (__bridge NSString *)(ABRecordCopyValue(record, kABPersonBirthdayProperty));
NSLog(#“B’day %#”, birthDay);
}
}
For an app I'm working on I need the users to be able to login to Facebook using the native SDK, but there is also a separate part of the app using an FB comments widget in a webview. The problem is after the user logs in using native SDK they are not logged in within the webview comments widget. Is there a way to have the user login using the native iOS SDK and then also log them into Facebook in a UIWebView. I tried using openAccessTokenFromData:completionHandler: in the FBSession class after the user signed in but couldn't get that to work, like below
- (void)didLogin
{
FBAccessTokenData *data = [FBAccessTokenData createTokenFromString:[FBSession activeSession].accessTokenData.accessToken
permissions:[FBSession activeSession].accessTokenData.permissions
expirationDate:[FBSession activeSession].accessTokenData.expirationDate
loginType:FBSessionLoginTypeWebView
refreshDate:nil];
[[FBSession activeSession] closeAndClearTokenInformation];
FBSession *session = [[FBSession alloc] init];
[session openFromAccessTokenData:data
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
}];
}
I used following code to open Facebook iOs SDK login in Webview in my native app and its works fine for me.
-(void)openFacebookAuthentication
{
NSArray *permission = [NSArray arrayWithObjects:kFBEmailPermission,kFBUserPhotosPermission, nil];
FBSession *session = [[FBSession alloc] initWithPermissions:permission];
[FBSession setActiveSession: [[FBSession alloc] initWithPermissions:permission] ];
[[FBSession activeSession] openWithBehavior:FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
switch (status) {
case FBSessionStateOpen:
[self getMyData];
break;
case FBSessionStateClosedLoginFailed: {
// prefer to keep decls near to their use
// unpack the error code and reason in order to compute cancel bool
NSString *errorCode = [[error userInfo] objectForKey:FBErrorLoginFailedOriginalErrorCode];
NSString *errorReason = [[error userInfo] objectForKey:FBErrorLoginFailedReason];
BOOL userDidCancel = !errorCode && (!errorReason || [errorReason isEqualToString:FBErrorLoginFailedReasonInlineCancelledValue]);
if(error.code == 2 && ![errorReason isEqualToString:#"com.facebook.sdk:UserLoginCancelled"]) {
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:kFBAlertTitle
message:kFBAuthenticationErrorMessage
delegate:nil
cancelButtonTitle:kOk
otherButtonTitles:nil];
[errorMessage performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
errorMessage = nil;
}
}
break;
// presently extension, log-out and invalidation are being implemented in the Facebook class
default:
break; // so we do nothing in response to those state transitions
}
}];
permission = nil;
}
Create facebook Appid Facebook Appid creating link Creating time follow facebook guide lines you must give bundile identfier in register time
Then Use this Code
#interface LoginViewController : UIViewController<UIWebViewDelegate>
#property(nonatomic,retain)UIWebView *webview;
#property (nonatomic, retain) NSString *accessToken;
#property(nonatomic,retain)UIActivityIndicatorView *FbActive;
#end
#interface LoginViewController ()
#end
#implementation LoginViewController
#synthesize accessToken,webview,FbActive;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Removeing the UIWebview Cookies
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(IBAction)fbLoginPage:(UIButton *)sender1
{
NSString *facebookClientID =facebookAppId;
NSString *redirectUri = #"http://www.facebook.com/connect/login_success.html";
NSString *extended_permissions=#"user_photos,user_videos,publish_stream,offline_access,user_checkins,friends_checkins,email";
NSString *url_string = [NSString stringWithFormat:#"https://graph.facebook.com/oauth/authorize?client_id=%#&redirect_uri=%#&scope=%#&type=user_agent&display=touch", facebookClientID, redirectUri, extended_permissions];
NSURL *url = [NSURL URLWithString:url_string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
CGRect webFrame =[self.view frame];
webFrame.origin.y = 0;
UIWebView *aWebView = [[UIWebView alloc] initWithFrame:webFrame];
[aWebView setDelegate:self];
self.webview = aWebView;
self.FbActive = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.FbActive.color=[UIColor darkGrayColor];
self.FbActive.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
[self.FbActive startAnimating];
[webview loadRequest:request];
[self.webview addSubview:self.FbActive];
[self.view addSubview:webview];
}
- (void)webViewDidFinishLoad:(UIWebView *)_webView {
/**
* Since there's some server side redirecting involved, this method/function will be called several times
* we're only interested when we see a url like: http://www.facebook.com/connect/login_success.html#access_token=..........
*/
//get the url string
[self.FbActive stopAnimating];
NSString *url_string = [((_webView.request).URL) absoluteString];
//looking for "access_token="
NSRange access_token_range = [url_string rangeOfString:#"access_token="];
//looking for "error_reason=user_denied"
NSRange cancel_range = [url_string rangeOfString:#"error_reason=user_denied"];
//it exists? coolio, we have a token, now let's parse it out....
if (access_token_range.length > 0) {
//we want everything after the 'access_token=' thus the position where it starts + it's length
int from_index = access_token_range.location + access_token_range.length;
NSString *access_token = [url_string substringFromIndex:from_index];
//finally we have to url decode the access token
access_token = [access_token stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//remove everything '&' (inclusive) onward...
NSRange period_range = [access_token rangeOfString:#"&"];
//move beyond the .
access_token = [access_token substringToIndex:period_range.location];
//store our request token....
self.accessToken = access_token;
//remove our window
// UIWindow* window = [UIApplication sharedApplication].keyWindow;
// if (!window) {
// window = [[UIApplication sharedApplication].windows objectAtIndex:0];
// }
[self.webview removeFromSuperview];
self.webview=nil;
//tell our callback function that we're done logging in :)
// if ( (callbackObject != nil) && (callbackSelector != nil) ) {
// [callbackObject performSelector:callbackSelector];
// }
//the user pressed cancel
}
else if (cancel_range.length > 0)
{
//remove our window
// UIWindow* window = [UIApplication sharedApplication].keyWindow;
// if (!window) {
// window = [[UIApplication sharedApplication].windows objectAtIndex:0];
// }
[self.webview removeFromSuperview];
self.webview=nil;
//tell our callback function that we're done logging in :)
// if ( (callbackObject != nil) && (callbackSelector != nil) ) {
// [callbackObject performSelector:callbackSelector];
// }
}
[self getuserdetailes];
}
-(void)getuserdetailes
{
NSString *action=#"me";
NSString *url_string = [NSString stringWithFormat:#"https://graph.facebook.com/%#?", action];
//tack on any get vars we have...
NSDictionary *get_vars=nil;
if ( (get_vars != nil) && ([get_vars count] > 0) ) {
NSEnumerator *enumerator = [get_vars keyEnumerator];
NSString *key;
NSString *value;
while ((key = (NSString *)[enumerator nextObject])) {
value = (NSString *)[get_vars objectForKey:key];
url_string = [NSString stringWithFormat:#"%#%#=%#&", url_string, key, value];
}//end while
}//end if
if (accessToken != nil)
{
//now that any variables have been appended, let's attach the access token....
url_string = [NSString stringWithFormat:#"%#access_token=%#", url_string, self.accessToken];
url_string = [url_string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#",url_string);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url_string]];
NSError *err;
NSURLResponse *resp;
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&resp error:&err];
NSString *stringResponse = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSLog(#"%#",stringResponse);
NSError* error;
NSDictionary *FBResResjson = [NSJSONSerialization
JSONObjectWithData:response//1
options:kNilOptions
error:&error];
NSLog(#"%#",FBResResjson);
}
}
thank to Divya Bhalodiya answered. this is the swift 3 version with Facebook SDK 4.x. If the code has any problem, feel free to edit & comment. Hope this will help.
func verifyFromWebView() {
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
fbLoginManager.logIn(withReadPermissions: ["email"], from: self) { (result, error) in
if error != nil {
print(error!.localizedDescription)
self.dismiss(animated: true, completion: nil)
}else if (result?.isCancelled)!{
print("Cancelled")
self.dismiss(animated: true, completion: nil)
}else{
if let fbLoginResult = result {
if fbLoginResult.grantedPermissions != nil && fbLoginResult.grantedPermissions.contains("email"){
self.getFBData()
}
}
}
}
}
I have a button, I want to connect to facebook as I click it, I want all the friends in my facebook.
I can access facebook and I get Facebook Token and saving it in Database.
I am connecting to facebook using following code in CONTROLLER A, but in CONTROLLER B, I want to fetch friend list.
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
NSString *fbAccessToken = [FBSession activeSession].accessTokenData.accessToken;
NSLog(#"Token is %#", fbAccessToken);
DataManager *dataManager = [[DataManager alloc] init];
/*flag is for identification, that from which account user has logged in, either facebook or
from account which he made using this app.
flag = facebook //if user is signed in using his facebook account
flag = myuser //if user is signed in using his own app account
*/
[dataManager saveloginData:fbAccessToken username:#"NO DATA" password:#"NO DATA" flag:#"facebook"];
// NSLog(#"Veer Suthar %#",user);
status = YES;
[self loginWithFacebookDirectly];
// here we use helper properties of FBGraphUser to dot-through to first_name and
// id properties of the json response from the server; alternatively we could use
// NSDictionary methods such as objectForKey to get values from the my json object
self.labelFirstName.text = [NSString stringWithFormat:#" %#", user.first_name];
// setting the profileID property of the FBProfilePictureView instance
// causes the control to fetch and display the profile picture for the user
self.profilePic.profileID = user.id;
NSLog(#"USER IS %#", user);
// self.loggedInUser = user;
}
Try this:
- (void)userFriendList {
NSString *query =#"SELECT name, pic_square FROM user WHERE uid in (SELECT uid2 FROM friend where uid1 = me())";
// Set up the query parameter
NSDictionary *queryParam =
[NSDictionary dictionaryWithObjectsAndKeys:query, #"q", nil];
// Make the API request that uses FQL
[FBRequestConnection startWithGraphPath:#"/fql"
parameters:queryParam
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (error) {
NSLog(#"Error: %#", [error localizedDescription]);
} else {
NSLog(#"Result: %#", result);
// show result
self.friendList = (NSArray *) [result objectForKey:#"data"];
}
}];
}
Here in the above self.friendList is a NSMutableArray
This is how I fetch Friend List in an Array and then placed in UITableView
Try like this....
// To fetch friends list
-(void)addList:(FBSession *)session
{
NSString* fql = [NSString stringWithFormat: #\"select uid from user where uid == %lld\", session.uid];
NSDictionary* params = [NSDictionary dictionaryWithObject:fql forKey:#\"query\"];
sessionView = session;
[[FBRequest requestWithDelegate:self] call:#\"facebook.friends.get\" params:params];
}
- (void)request:(FBRequest*)request didLoad:(id)result
{
if(myList==nil)
{
NSArray* users = result;
myList =[[NSArray alloc] initWithArray: users];
for(NSInteger i=0;i<[users count];i++) {
NSDictionary* user = [users objectAtIndex:i];
NSString* uid = [user objectForKey:#\"uid\"];
NSString* fql = [NSString stringWithFormat: #\"select name from user where uid == %#\", uid];
NSDictionary* params = [NSDictionary dictionaryWithObject:fql forKey:#\"query\"];
[[FBRequest requestWithDelegate:self] call:#\"facebook.fql.query\" params:params];
}
}
else
{
NSArray* users = result;
NSDictionary* user = [users objectAtIndex:0];
NSString* name = [user objectForKey:#\"name\"];
txtView.text=[NSString localizedStringWithFormat:#\"%#%#,\n\",txtView.text,name];
}
}
//To list the online friends
- (void)session:(FBSession*)session didLogin:(FBUID)uid {
NSString *fql = [NSString localizedStringWithFormat:#\"SELECT uid FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=%lld) AND 'active' IN online_presence\",uid];
myList=nil;
NSDictionary *params =[NSDictionary dictionaryWithObject:fql forKey:#\"query\"];
[[FBRequest requestWithDelegate:self] call:#\"facebook.fql.query\" params:params];
}
- (void)request:(FBRequest*)request didLoad:(id)result {
if(myList==nil) {
NSArray* users = result;
myList =users;
for(int i=0;i<[users count];i++) {
NSDictionary* user = [users objectAtIndex:i];
NSString* name = [user objectForKey:#\"uid\"];
NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:name,#\"uids\",#\"name\",#\"fields\",nil];
[[FBRequest requestWithDelegate:self] call:#\"facebook.users.getInfo\" params:params];
}
}
else {
NSArray* users = result;
NSDictionary* user = [users objectAtIndex:0];
NSString* name = [user objectForKey:#\"name\"];
NSLog(name);
}
}
Hope it will helps you..
I want to publish an Open Graph fitness:runs action on Facebook and I want it to render with a map of my path. The path is defined by the path coordinates below. How do I do this? The method below publishes the action and I can see the text for the action in my Activity Log on Facebook and in my timeline. But I do not see a map when I hoover over any element of the posted action. What am I doing wrong?
- (void) fbPost:(NSString *)txt toList:(NSString *)listId { // post
[FBSession setActiveSession:[FacebookManager instance].facebook.session];
NSMutableDictionary<FBGraphObject> *action = [FBGraphObject graphObject];
action[#"course"] = #"http://samples.ogp.me/48586838281818";
action[#"privacy"] = privacyStr;
NSMutableArray *coords = [NSMutableArray arrayWithCapacity:59];
for (int i = 0; i < 59; i++)
{
NSMutableDictionary *coord = [[NSMutableDictionary alloc] initWithCapacity:3];
#define TIMESTAMP #"fitness:metrics:timestamp"
#define LATITUDE #"fitness:metrics:location:latitude"
#define LONGITUDE #"fitness:metrics:location:longitude"
[coord setValue:[NSString stringWithFormat:#"2013-04-01T12:%2d:00+0000", i] forKey:TIMESTAMP];
[coord setValue:[NSString stringWithFormat:#"%f", 37.442564 + i * 0.00001] forKey:LATITUDE];
[coord setValue:[NSString stringWithFormat:#"%f", -122.164879 + i * 0.000001] forKey:LONGITUDE];
[coords addObject:coord];
NSLog(#"coord=%# i=%d", coord, i);
}
action[#"path"] = [coords JSONString];
action[#"message"] = txt;
[FBRequestConnection startForPostWithGraphPath:#"me/fitness.runs"
graphObject:action
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (!error) // it's a post, save id
{
}
else
{
}
}];
}
NSMutableDictionary<FBGraphObject> *action = [FBGraphObject graphObject];
NSMutableDictionary<FBGraphObject> *course = [FBGraphObject openGraphObjectForPost];
course[#"og:title"] = #"My Workout";
course[#"og:type"] = #"fitness.course"; //very important
course[#"og:url"] = #"www.fitness.com"; // give a meaningful url here
course[#"fitness:duration:value"] = #"3000";
course[#"fitness:duration:units"] = #"s";
course[#"fitness:calories"] = #"100";
course[#"fitness:distance:value"] = 1.7;
course[#"fitness:distance:units"] = #"mi";
course[#"fitness:speed:value"] = #"2";
course[#"fitness:speed:units"] = #"m/s";
course[#"fitness:pace:value"] = #"0.5";
course[#"fitness:pace:units"] = #"s/m";
course[#"og:description"] = #"course_description";
NSMutableArray *locationDataPointsArray = [[NSMutableArray alloc] init];
locationDataPointsArray[0] = #{#"location:latitude": 12.91277, #"location:longitude": 77.56671};
locationDataPointsArray[1] = #{#"location:latitude": 12.91284, #"location:longitude": 77.56681};
locationDataPointsArray[2] = #{#"location:latitude": 12.91297, #"location:longitude": 77.56691};
course[#"fitness:metrics"] = locationDataPointsArray;
action[#"fb:explicitly_shared"] = #"true";
action[#"course"] = course;
NSString *path = #”me/fitness.runs”;
//for custom story: NSString *path = #”me/urNamespace:name of ur action”;
[FBRequestConnection startForPostWithGraphPath:path graphObject:action completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSLog(#"Posted fitness action, id: %#", [result objectForKey:#"id"]);
NSString *alertText = #"Workout successfully posted to Facebook :)";
NSString *alertTitle = #"Success";
[[[UIAlertView alloc] initWithTitle:alertTitle message:alertText delegate:nil cancelButtonTitle:#"OK!" otherButtonTitles:nil] show];
}
else {
NSLog(#"error in posting action %#", error.description);
}
}];
I don't really know how to answer you question however I read some documents the other day and they may be useful to you...
I would Recommend
That you take a read of this document and hopefully you will be able to understand how to integrate this in your app.
You may also want to take a read of this
& this
Happy coding :)