I use the latest Facebook SDK (3.1.1).
I wrote a function in my AppDelegate that checks the current session and creates or opens a session according to the state.
The second conditions always return NO and go to show login screen.
I don't understand way.
AppDelegate method:
if (facebook.isOpen == NO)
{
facebook = [[FBSession alloc] initWithPermissions:permission];
if (facebook.state == FBSessionStateCreatedTokenLoaded)
{
[facebook openWithCompletionHandler:^(FBSession *session,FBSessionState status,NSError *error)
{
// load user details
}];
}
else
{
// show login screen
}
}
There is a property access token in the FbSession class.
Use that string in order to check the login status.
If you are getting null in that string it means your session is expired but if you are getting some value in that is means you are still login in the app and you can do what ever you want.
Regards
Abhishek Goyal
if the session state equal to FBSessionStateCreatedTokenLoaded it means that there is a cached accessToken, a call to open* will result in an open session, without UX or app-switching
-(BOOL)validFBSessionExists{
if (FBSession.activeSession.isOpen){
NSLog(#"Facebook accessToken:%#",FBSession.activeSession.accessTokenData.accessToken);
return YES;
}
else if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded)
{
// Cached token exist, Session needs to be re-opened
[FBSession.activeSession openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent completionHandler:nil];
return YES;
}
return NO;
}
Related
We're developing an iOS app in objective-C and we've decided to add Firebase in our app. We also decided to add the GoogleMobileAds framework that comes with it in order to have Rewarded Video Ads.
I've implemented AdMob as detailed in the official guide but I can't figure out how to get the ad unit ID in each of the callbacks.
The only parameter I have is of type GADRewardBasedVideoAd and it doesn't seem to have any accessible data that provide the ad ID.
Here's one of the callbacks:
- (void)rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd
{
NSLog(#"Opened reward based video ad.");
}
I need the ad ID because we're using several ads in our app and I need to know which one is ready/opened/completed/failed/etc.
I tried to use rewardBasedVideoAd.adMetadata[#"AdId"] but it returns nil.
Any help would be appreciated.
Thanks
here:
#implementation GameViewController{
GADRewardedAd *gameOverRewardedAd, *extraCoinsRewardedAd;
}
-(void)viewDidLoad{
gameOverRewardedAd = [self createAndLoadRewardedAdForAdUnit:#"ca-app-pub-YOURID"];
extraCoinsRewardedAd = [self createAndLoadRewardedAdForAdUnit:#"ca-app-pub-YOURID"];
}
-(GADRewardedAd *)createAndLoadRewardedAdForAdUnit:(NSString *) adUnitId {
GADRewardedAd *rewardedAd = [[GADRewardedAd alloc] initWithAdUnitID:adUnitId];
GADRequest *request = [GADRequest request];
[rewardedAd loadRequest:request completionHandler:^(GADRequestError * _Nullable error) {
if (error) {
// Handle ad failed to load case.
} else {
// Ad successfully loaded.
}
}];
return rewardedAd;
}
Then:
#pragma mark admob reward Ad delegate
- (void)rewardedAdDidDismiss:(GADRewardedAd *)rewardedAd {
//NSLog(#"rewardedAdDidDismiss:");
if (rewardedAd == gameOverRewardedAd) {
//do your things here
}else if (rewardedAd == extraCoinsRewardedAd){
}
}
Hope this helps.
I am using FBSDKLoginManager to log in with facebook manually.
I have created a native method in which I have the following code:
#import "FacebookLoginManager.h"
#import "FBSDKCoreKit/FBSDKCoreKit.h"
#import "FBSDKLoginKit/FBSDKLoginKit.h"
#implementation FacebookLoginManager
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(newSession:(RCTResponseSenderBlock)callback) {
dispatch_async(dispatch_get_main_queue(), ^{
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:#[#"public_profile", #"email"] fromViewController: nil handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
callback(#[#"Error", [NSNull null]]);
} else if (result.isCancelled) {
callback(#[#"Canceled", [NSNull null]]);
} else {
FBSDKAccessToken *token = result.token;
NSString *tokenString = token.tokenString;
NSString *userId = token.userID;
NSDictionary *credentials = #{ #"token" : tokenString, #"userId" : userId };
callback(#[[NSNull null], credentials]);
}
}];
});
};
The code works mostly but sometimes it does not trigger the login window (safari) although it does not throw any error.
Besides, the handler code is never called in this case. If I close the app and reopen it everything works again.
The problem happens after a period of time passes so I suspect it may be related to some timeout or token expiration somewhere but I am not sure.
How can I fix this problem, any idea?
I realized that revoking the facebook access token on logout solved the problem. Perhaps the FBSDKLoginManager had some problems when the user had already authorized your app and you try to ask for authorization again.
To revoke the token I make a remote call to my server which has facebook's PHP SDK:
<?php
$fb = new Facebook(array(
'app_id' => 'YOUR_FB_APP_ID',
'app_secret' => 'YOUR_FB_APP_SECRET',
'default_graph_version' => 'v2.5',
));
try
{
$userFbToken = 'user-fb-token-stored-somewhere';
$appsecret_proof = hash_hmac('sha256', $userFbToken, 'YOUR_FB_APP_SECRET');
$response = $fb->delete("/me/permissions", array('appsecret_proof'=>$appsecret_proof),$userFbToken);
}
catch(FacebookResponseException $e)
{
// exception handling...
}
In my project user get logged-in through Facebook native app(FBConnect) and i kept the user's access token. In my app i have a like button which display the iframe of a facebookpage like button and now when user press the page like button the facebook ask the user to sign in again.
Although the user is currently logged-in and the token is validand i am passing the access token in the iframe
Here is the code which i am using for creating iframe.
NSString *str=[NSString stringWithFormat:#"<iframe src=\"https://www.facebook.com/plugins/likebox.php?id=XXXXXXXXXXXXXXX&access_token=%#&width=292&connections=0&stream=false&header=false&height=62\" scrolling=\"no\" frameborder=\"0\" style=\"border:none; overflow:hidden; width:282px; height:62px;\" allowTransparency=\"true\"></iframe>",accesstoken];
NSString *likeButtonHtml = [NSString stringWithFormat:#"<HTML><BODY>%#</BODY></HTML>", str];
[webview loadHTMLString:likeButtonHtml baseURL:[NSURL URLWithString:#""]];
Please tell me how will i avoid this second login for like a page on iframe
You should log in with UIWebView. You can do it with implementing method:
[[FBSession activeSession] openWithBehavior:FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
switch (status) {
case FBSessionStateOpen:
// call the legacy session delegate
//Now the session is open do corresponding UI changes
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]);
// call the legacy session delegate if needed
//[[delegate facebook] fbDialogNotLogin:userDidCancel];
}
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
}
}];
Check my sample code:https://github.com/gneil90/facebook-likebox-ios-login
A quick question about the Facebook SDK for iOS. I'm trying to implement the functionality for a user to grant my app access to extended permissions.
While the code works fine when the user accepts the extended permission request (from the Facebook iOS app the user gets redirected to), I'm having trouble detecting when the user has returned to the app while having neither accepted the permissions, or clicking cancel then returning to the app.
If I click 'Cancel' and multitask back into the app, there is nothing logged and nothing shown on-screen. When I try to re-authorize again, the Facebook SDK throws an exception:
FBSession: It is not valid to reauthorize while a previous reauthorize call has not yet completed.
While I can catch the exception, it still doesn't help as I can't figure out how to stop the previous call in order to allow the user to try to re-authorize again.
Here's the code I'm using at the moment:
#try {
[[FBSession activeSession] reauthorizeWithPermissions:[self requiredPermissions]
behavior:FBSessionLoginBehaviorWithFallbackToWebView
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
[self fetchUserPermissionsWithCompletionHandler:^(BOOL extendedGranted) {
if (extendedGranted) {
[self setCanPostToActivityStream:YES];
}
}];
}
else {
NSLog(#"%#", [error localizedDescription]);
}
}];
}
#catch (NSException *exception) {
NSLog(#"%#", exception);
}
Now, the issue is not with the code above - the code works fine. The issue I'm having, again, is cancelling the previous re-authorize call when the user doesn't return to the app successfully after allowing permissions. The Facebook SDK as it is doesn't seem to alert the app of this situation in any way.
From your App's delegate, when - (void)applicationDidBecomeActive:(UIApplication*)application is invoked, you need to make a call to the active session's handleDidBecomeActive method. Once you do that, the completion handler associated with your reauthorizeWithPublishPermissions call will be invoked, and will provide the appropriate error.
- (void)applicationDidBecomeActive:(UIApplication*)application
{
[[FBSession activeSession] handleDidBecomeActive];
}
What you should see coming back from the SDK is an error from the reauthorize completion handler.
{
"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorReauthorizeFailedReasonUserCancelled";
}
I tested something similar with the following setup: Xcode 4.5, iOS 5, Authorize against m.facebook.com. After an initial authorization I put code to hit reauthorize, then clicked Cancel and got this error.
[FBSession.activeSession
reauthorizeWithPublishPermissions:
[NSArray arrayWithObject:#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
// Success case
} else {
// Check the error, info
// [[error userInfo] objectForKey:#"com.facebook.sdk:ErrorLoginFailedReason"]
}
}];
If you are not seeing this then it is a bug and you may want to file it with Facebook.
I'm trying to add Beeblex's new In App Purchase verification to my app, however i'm struggling passing a return value from within a block.
Here's the code I have now, and as you can see I set a BOOL value, then within the verification block I set the BOOL and return it at the end. However the return at the end is called before the block finishes, so what I need is to return the BOOL from within the block?
- (BOOL)verifyTransaction:(SKPaymentTransaction *)transaction
{
if (![BBXIAPTransaction canValidateTransactions]) {
return YES; // There is no connectivity to reach the server
}
BOOL __block toReturn = YES;
BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transaction];
bbxTransaction.useSandbox = YES;
[bbxTransaction validateWithCompletionBlock:^(NSError *error) {
if (bbxTransaction.transactionVerified) {
if (bbxTransaction.transactionIsDuplicate) {
// The transaction is valid, but duplicate - it has already been sent to Beeblex in the past.
NSLog(#"Transaction is a duplicate!");
[FlurryAnalytics logEvent:#"Transaction duplicate!"];
toReturn = NO;
} else {
// The transaction has been successfully validated and is unique
NSLog(#"Transaction valid data:%#",bbxTransaction.validatedTransactionData);
[FlurryAnalytics logEvent:#"Transaction verified"];
toReturn = YES;
}
} else {
// Check whether this is a validation error, or if something went wrong with Beeblex
if (bbxTransaction.hasConfigurationError || bbxTransaction.hasServerError || bbxTransaction.hasClientError) {
// The error was not caused by a problem with the data, but is most likely due to some transient networking issues
NSLog(#"Transaction error caused by network, not data");
[FlurryAnalytics logEvent:#"Transaction network error"];
toReturn = YES;
} else {
// The transaction supplied to the validation service was not valid according to Apple
NSLog(#"Transaction not valid according to Apple");
[FlurryAnalytics logEvent:#"Transaction invalid!!"];
toReturn = NO;
}
}
}];
NSLog(#"toReturn: %#",toReturn ? #"Yes" : #"No");
return toReturn;
}
If I simply put return = NO; inside the block, I get compiler warnings of Incompatible block pointer types, and control may reach end of non-void block.
Beeblex developer here. The code inside -validateWithCompletionBlock: execute asynchronously (in the background, it needs to talk to our servers, so there's no point blocking your app completely while we wait for the Internet to do its thing).
Therefore, you need to rethink your approach to validating your receipts. Right now you, general workflow is:
Complete purchase
Call Beeblex
Wait for response
Check boolean value
But #3 returns right away, so this will never work. You should consider doing something like this:
Complete purchase
Show a “Please wait…” view, or something similar that advises the user that you're unlocking whatever they've purchased.
Call Beeblex
Inside the block, determine whether the validation succeeded or not, and then act to unlock the content from there.
Sit idle until called by the block
Here's a quick-and-dirty example. I didn't compile it, so it probably has a few bugs, but it should give you the idea behind the intended usage pattern.
- (void) showWaitView {
// Display a wait view
}
- (void) hideWaitView {
// Hide the wait view
}
- (void) completeValidationWithValidateReceiptData:(NSDictionary *) receipt isRecoverableError(BOOL) isRecoverableError {
[self hideWaitView];
if (receipt) {
// Unlock the content, tell the user
} else {
if (isRecoverableError) {
// Probably a network error of some kind. Tell user they need to be connected,
// and ask them to do it again.
} else {
// Keep the content locked, tell the user something went wrong
}
}
}
- (void) validateReceipt:(SKPaymentTransaction *) transaction {
if (![BBXIAPTransaction canValidateTransactions]) {
[self completeValidationWithValidateReceiptData:Nil isRecoverableError:YES];
return;
}
BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transaction];
bbxTransaction.useSandbox = YES;
[bbxTransaction validateWithCompletionBlock:^(NSError *error) {
if (bbxTransaction.transactionVerified) {
if (bbxTransaction.transactionIsDuplicate) {
// The transaction is valid, but duplicate - it has already been sent to Beeblex in the past.
[FlurryAnalytics logEvent:#"Transaction duplicate!"];
[self completeValidationWithValidateReceiptData:Nil isRecoverableError:NO];
} else {
// The transaction has been successfully validated and is unique
[FlurryAnalytics logEvent:#"Transaction verified"];
[self completeValidationWithValidateReceiptData:bbxTransaction.validatedTransactionData isRecoverableError:NO];
}
} else {
// Check whether this is a validation error, or if something went wrong with Beeblex
if (bbxTransaction.hasConfigurationError || bbxTransaction.hasServerError || bbxTransaction.hasClientError) {
// The error was not caused by a problem with the data, but is most likely due to some transient networking issues
[FlurryAnalytics logEvent:#"Transaction network error"];
[self completeValidationWithValidateReceiptData:Nil isRecoverableError:YES];
} else {
// The transaction supplied to the validation service was not valid according to Apple
[FlurryAnalytics logEvent:#"Transaction invalid!!"];
[self completeValidationWithValidateReceiptData:Nil isRecoverableError:NO];
}
}
}];
}
<3 blocks
- (void)verifyTransaction:(SKPaymentTransaction *)transaction completionHandler:(void (^)(BOOL flag))completionHandler
{
if (![BBXIAPTransaction canValidateTransactions]) {
completionHandler(YES); // There is no connectivity to reach the server
}
BBXIAPTransaction *bbxTransaction = [[BBXIAPTransaction alloc] initWithTransaction:transaction];
bbxTransaction.useSandbox = YES;
[bbxTransaction validateWithCompletionBlock:^(NSError *error) {
if (bbxTransaction.transactionVerified) {
if (bbxTransaction.transactionIsDuplicate) {
// The transaction is valid, but duplicate - it has already been sent to Beeblex in the past.
NSLog(#"Transaction is a duplicate!");
[FlurryAnalytics logEvent:#"Transaction duplicate!"];
completionHandler(NO);
} else {
// The transaction has been successfully validated and is unique
NSLog(#"Transaction valid data:%#",bbxTransaction.validatedTransactionData);
[FlurryAnalytics logEvent:#"Transaction verified"];
completionHandler(YES);
}
} else {
// Check whether this is a validation error, or if something went wrong with Beeblex
if (bbxTransaction.hasConfigurationError || bbxTransaction.hasServerError || bbxTransaction.hasClientError) {
// The error was not caused by a problem with the data, but is most likely due to some transient networking issues
NSLog(#"Transaction error caused by network, not data");
[FlurryAnalytics logEvent:#"Transaction network error"];
completionHandler(YES);
} else {
// The transaction supplied to the validation service was not valid according to Apple
NSLog(#"Transaction not valid according to Apple");
[FlurryAnalytics logEvent:#"Transaction invalid!!"];
completionHandler(NO);
}
}
}];
}
Then use
[instance verifyTransaction:transaction completionHandler:^(BOOL flag) {
if (flag) {
// YOUR CODE HERE
}
}];
instead of
if ([instance verifyTransaction:transaction]) {
// YOUR CODE HERE
}