ios Facebook SDK v4.x access token null despite being logged in via FBSDKLoginButton - facebook-ios-sdk

My facebook access token is null despite the fact that the button shows that I'm logged in. Anyone know why this would be?
From RootViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self->login_button = [[FBSDKLoginButton alloc] init];
self->login_button.center = self.view.center;
[self.view addSubview:self->login_button];
FBSDKAccessToken* access_token =[FBSDKAccessToken currentAccessToken];
NSLog(#"Access Token, %#",access_token);
}
From ApplicationDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setRootViewController:[[RootViewController alloc] init]];
[self.window makeKeyAndVisible];
[self.window setBackgroundColor:[UIColor purpleColor]];
[FBSDKLoginButton class];
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
also my -ObjC linker flag is set

The FBSDKApplicationDelegate needs to be called first to resolved cached tokens. Since you are setting the root view controller immediately, that calls your viewDidLoad before the FBSDKApplicationDelegate. Instead, you can move the FBSDKApplicationDelegate up:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[FBSDKLoginButton class];
BOOL r = [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setRootViewController:[[RootViewController alloc] init]];
[self.window makeKeyAndVisible];
[self.window setBackgroundColor:[UIColor purpleColor]];
return r;
}

I got same issue today, only because I missed one step:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
return handled;
}

I have had an issue where I could not get the Access Token because I am calling it before return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];. I needed to check the access token to check whether the user is logged in or not to decide which should be my root view controller. What I did was I tried to get the access token from the cache manually in application didFinishLaunchingWithOptions by:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
FBSDKProfile *cachedProfile = [FBSDKProfile fetchCachedProfile];
[FBSDKProfile setCurrentProfile:cachedProfile];
FBSDKAccessToken *cachedToken = [[FBSDKSettings accessTokenCache] fetchAccessToken];
NSLog(#"Cached Token: %#", cachedToken.tokenString);
if (cachedToken) {
//User is logged in, do logic here.
}
return [[FBSDKApplicationDelegate sharedInstance] application:application
didFinishLaunchingWithOptions:launchOptions];
}
You can see this method by checking the implementation of - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions in FBSDKApplicationDelegate.m
UPDATE
This does not seem to work anymore. I'm not sure why. But it seems that the fetchCachedProfile and accessTokenCache methods have been made internal. I could not access these methods anymore in my code as it gives me an error.

You could build the logic around waiting for FBSDKAccessTokenDidChangeNotification notification in your ViewController's code.
In your AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ...
// add observer BEFORE FBSDKApplicationDelegate's - application:didFinishLaunchingWithOptions: returns
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(fbAccessTokenDidChange:)
name:FBSDKAccessTokenDidChangeNotification
object:nil];
return [[FBSDKApplicationDelegate sharedInstance] application: application didFinishLaunchingWithOptions: launchOptions];
}
- (void)fbAccessTokenDidChange:(NSNotification*)notification
{
if ([notification.name isEqualToString:FBSDKAccessTokenDidChangeNotification]) {
if ([FBSDKAccessToken currentAccessToken]) {
// token is ready to be used in the app at this point
}
}
}

It happened to me too, the "Login with Facebook" button was working as expected, I was being asked for permissions, allowed them, the application:openURL:... delegate method was being called with an URL that seemed valid, nonetheless the accessToken property remained nil.
The problem in my case was the fact that somehow Shared Keychain was removed from the application entitlements, thus the Facebook SDK was not able to save the token in keychain. Enabling the capability solved the problem.
The odd thing is that this was a silent error, so it was not clear from the beginning why the access token wasn't being set...

You should use tokenString instead, like this:
FBSDKAccessToken* access_token =[FBSDKAccessToken currentAccessToken].tokenString;
NSLog(#"Access Token, %#",access_token);

Related

How to setup react-native-navigation with react-native-siri-shortcut (rootView issue)

Need some guidance on how to get RNN to work with react-native-siri-shortcut.
Please bear with me if my post is too verbose as I'm a noob with xCode and objective C, so don't want to miss anything out.
So I've got a couple of questions/problems with trying to get the 2 libraries to work together:
1. Setting up the initialProperties with a RCTRootView
react-native-siri-shortcuts sets up the app in AppDelegate.m like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
// Check if the app launched with any shortcuts
BOOL launchedFromShortcut = [launchOptions objectForKey:#"UIApplicationLaunchOptionsUserActivityDictionaryKey"] != nil;
//Add a boolean to the initialProperties to let the app know you got the initial shortcut
NSDictionary *initialProperties = #{ #"launchedFromShortcut":#(launchedFromShortcut) };
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:#"doesthismatter"
initialProperties:initialProperties // Add the initial properties here
launchOptions:launchOptions];
...
}
In V2 of RNN, the rootView is not being used anymore, instead we just call:
[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions];
Therefore we are not able to pass the initialProperties through to the rootView.
2. Accessing the rootView in other parts of the code
Continuing on from the set up react-native-siri-shortcut:
// This method checks for shortcuts issued to the app
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler
{
UIViewController *viewController = [self.window rootViewController];
RCTRootView *rootView = (RCTRootView*) [viewController view];
// If the initial properties say the app launched from a shortcut (see above), tell the library about it.
if ([[rootView.appProperties objectForKey:#"launchedFromShortcut"] boolValue]) {
ShortcutsModule.initialUserActivity = userActivity;
rootView.appProperties = #{ #"launchedFromShortcut":#NO };
}
[ShortcutsModule onShortcutReceivedWithUserActivity:userActivity];
return YES;
}
Since RCTRootView isn't being used to register our app, will this part of the code work?
I've already searched the repo for issues related to initialProps and reactView and the few issues that I did find, never got answered and were closed due to inactivity.
Any help would be greatly appreciated.
Actually, searching for the launchedFromShortcut property on github I realised it's not consumed in the Javascript side. Therefore it's only usage is as flag to tell wether the app was launched from a shortcut and passing it to the RNN's RNNReactRootViewCreator seems unnecessary.
Removed that necessity the integration turned out to be pretty simple, we only need to add launchedFromShortcut = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey] != nil; to didFinishLaunchingWithOptions and check it's value on continueUserActivity.
The AppDelegate.m would look as follows:
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <ReactNativeNavigation/ReactNativeNavigation.h>
#import <RNSiriShortcuts/RNSiriShortcuts-Swift.h>
#implementation AppDelegate
BOOL launchedFromShortcut;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
launchedFromShortcut = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey] != nil;
[ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions];
//
// Regular RNN bootstrap code omitted for brevity sake
//
return YES;
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler
{
if (launchedFromShortcut) {
ShortcutsModule.initialUserActivity = userActivity;
launchedFromShortcut = NO;
}
[ShortcutsModule onShortcutReceivedWithUserActivity:userActivity];
return YES;
}
#end

Logging in to Facebook pre iOS 9

I am using Xcode 7 to write an app targeted to iOS7, using Facebook iOS idk v4.1. When I execute a facebook login, [FBSDKAccessToken currentAccessToken] is still nil in the FBSDKLoginButton callback. I have tried several SO solutions for fixing this, but none has worked for me so far.
I think the problem is in my appDelegate. Here is my code:
- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
//for Facebook signin
if([[FBSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:sourceApplication annotation:annotation])
{
return YES;
}
//for Google Signin on iOS 8 and earlier
else
{
NSDictionary* options = #{UIApplicationOpenURLOptionsSourceApplicationKey:sourceApplication, UIApplicationOpenURLOptionsAnnotationKey:annotation};
return [self application:application openURL:url options:options];
}
}
//iOS 9 and later
//for Google Signin
- (BOOL) application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
if ([[[NSUserDefaults standardUserDefaults] objectForKey:UDLoginType] integerValue] == ltGoogle)
{
return [[GIDSignIn sharedInstance] handleURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey]];
}
else
{
return YES;
}
}
When my app returns from Facebook, application:openURL:options (iOSv9+) is called. That method contains no calls to the Facebook sdk. There is a call to the Facebook sdk in application:openURL:sourceApplication:annotation (iOSv8-), but I don’t know how to translate that into the iOSv9 call, as Google does. So as far as I can tell, there’s no processing of the login URL after a Facebook login. What am I doing wrong?
Thanks
Here is how you could implement for ios 9+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options {
return [[FBSDKApplicationDelegate sharedInstance] application:app
openURL:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]
|| [[GIDSignIn sharedInstance] handleURL:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
annotation:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
}
for below ios 9
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation
] ||
[[GIDSignIn sharedInstance] handleURL:url
sourceApplication:sourceApplication
annotation:annotation];
}

FBLoginView: login isn't performed

I'm adding FBLoginView to my ViewController < FBLoginViewDelegate >:
FBLoginView *loginview = [[FBLoginView alloc] init];
loginview.frame = CGRectOffset(loginview.frame, 5, 5);
loginview.delegate = self;
[self.view addSubview:loginview];
[loginview sizeToFit];
All the necessary fields in plist (FacebookAppID, FacebookDisplayName, URL Schemes) are all set according to the tutorial. The facebook app is also configured according to the tutorial (bundle ID is set, Facebook login is enabled).
But the login still isn't performed. When I press on "log in", I get redirected to the browser with facebook login, but when it's finished, I'm not logged in the app (loginViewFetchedUserInfo:user: isn't called, "log in" hasn't changed to "log out").
What can be the problem?
Everything worked after I implemented the following in the AppDelegate.m (taken from one of the official examples):
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen:
if (!error) {
// We have a valid session
//NSLog(#"User session found");
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *error) {
if (!error) {
self.loggedInUserID = user.id;
self.loggedInSession = FBSession.activeSession;
}
}];
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
[[NSNotificationCenter defaultCenter]
postNotificationName:FBSessionStateChangedNotification
object:session];
if (error) {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
/*
* Opens a Facebook session and optionally shows the login UX.
*/
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
return [FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
/*
*
*/
- (void) closeSession {
[FBSession.activeSession closeAndClearTokenInformation];
}
/*
* If we have a valid session at the time of openURL call, we handle
* Facebook transitions by passing the url argument to handleOpenURL
*/
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
}
You need to add the following to the app delegate
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// Call FBAppCall's handleOpenURL:sourceApplication to handle Facebook app responses
BOOL wasHandled = [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
// You can add your app-specific url handling code here if needed
return wasHandled;
}
You may need to implement the
(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url method
Be sure to call:
return [FBSession.activeSession handleOpenURL:url];
When applicable.
#Sergey: Are you want to open FBDialog on your native app or in browser? If you to want open in your native app then use "FBSessionLoginBehaviorForcingWebView". Here is my code that I am using:
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 yourmethod];
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) {
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:#"FBAlertTitle"
message:#"FBAuthenticationErrorMessage"
delegate:nil
cancelButtonTitle:#"Ok"
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;
or you want to open in browser then use following :
In your .h file
#import <FacebookSDK/FacebookSDK.h> and add FBLoginViewDelegate delegate
In you .m file
FBLoginView *loginview = [[FBLoginView alloc] init];
loginview.frame = CGRectOffset(loginview.frame, 5, 5);
loginview.delegate = self;
[self.view addSubview:loginview];
[loginview sizeToFit];
// use following delegate methods
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
// first get the buttons set for login mode
}
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
// 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
NSLog(#"userprofile:%#",user);
}
- (void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView {
//BOOL canShareAnyhow = [FBNativeDialogs canPresentShareDialogWithSession:nil];
}
- (void)loginView:(FBLoginView *)loginView handleError:(NSError *)error {
// see https://developers.facebook.com/docs/reference/api/errors/ for general guidance on error handling for Facebook API
// our policy here is to let the login view handle errors, but to log the results
NSLog(#"FBLoginView encountered an error=%#", error);
}

didFailLoadWithError incorrectly reports no connection

I have an app with a standard view controller with multiple buttons on it. Each button links to a separate view controller with a unique UIWebView. Each UIWebView has didFailLoadWithError implemented and it seems to work fine: when I turn off wifi, and try to load the UIWebView from the main view controller page, I correctly get the error message from didFailLoadWithError. When I turn on wifi and load the UIWebView, it works fine - no error. However, when I click on a link within that UIWebView page, I get the didFailLoadWithError error again. Even more interesting, I clear the error message and the new page still loads from the link I just clicked, so I know the connection is good. Here is my implementation...does anyone know of a way to force didFailLoadWithError to only run once on first load and suppress it from running again once you verify a web connection is good?
#synthesize webView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Network Alert" message:#"No Internet Connection - Please Check Your Network Settings and Try Again" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alert show];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: #"http://www.site.com/index.html"]]];
[webView addSubview:activity];
timer=[NSTimer scheduledTimerWithTimeInterval:(1.0/2.0)
target:self selector:#selector(loading) userInfo:nil repeats:YES];
}
- (void)loading {
if (!webView.loading)
[activity stopAnimating];
else
[activity startAnimating];
}
I just had this problem. Whats going on, is when you click on a link within the web view, while the page is still loading, you will get error -999. This translates to NSURLErrorCancelled.
You can find out more in the following link. Goto the URL Loading System Error Codes section. https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html
In my code i was telling an alert view to pop up saying the internet connect was lost upon -webView:didFailLoadWithError: being called. I wrapped that code around a condition on the error object. Heres an example.
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if ([error code] == NSURLErrorNotConnectedToInternet || [error code] == NSURLErrorNetworkConnectionLost) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"Check internet connection." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
}

uitabbarcontroller / uitabbar in navigation based project

I have created navigation based project. and in second screen i want to add uitabbarcontroller. so can any one suggest how i do this.
i already did lot of search but no success yet. so please can you provide a simple sample of this. i already tried below discussion but i think its not a good approach.
Navigation Based Application with TabBar
Thanks
Actually this is the correct approach. The one thing that is not correct is where the controllers are allocated. This is happened in the previous controller, the one that is making the push, but should be allocated in the object that is responsible, the TabBarController.
When you implement your action to show the UITabBarController make the following code:
- (void) theAction {
SomeTabBarControllerSubClass *controller = [[SomeTabBarControllerSubClass alloc] init];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
Then when you implement the SomeTabBarControllerSubClass class:
(.h)
#interface SomeTabBarControllerSubClass : UITabBarController {
UIViewController *first;
UIViewController *second;
}
#end
(.m)
#implementation SomeTabBarControllerSubClass
- (void) viewDidLoad {
first = [[UIViewController alloc] init]; //Or initWithNib:
second = [[UIViewController alloc] init];
first.view.backgroundColor = [UIColor greenColor] //Just example
second.view.backgroundColor = [UIColor redColor] //Just example
first.tabBarItem.image = [UIImage imageNamed:#"someImage.png"];
self.viewControllers = [NSArray arrayWithObjects:first,second,nil];
}
- (void) dealloc {
[first dealloc];
[second dealloc];
[super dealloc];
}
#end

Resources