OAuth2.0 refresh token never completes in background fetch - ios

I am trying to check emails on gmail while app is in background.
I have to deal with OAuth2 to get to the gmail, so 1st I am refreshing the token. Then in finishedRefreshWithFetcher, if no error I am checking emails using Mailcore2.
It all works fine if app is in foreground, or in -(void) applicationDidBecomeActive: (UIApplication *) application {...}.
But in background fetch it never gets to -(void)auth:finishedRefreshWithFetcher:error:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[self startOAuth2];
completionHandler(UIBackgroundFetchResultNewData);
}
- (void) startOAuth2{
static NSString *const kKeychainItemName = #"Google OAuth2 For MyApp";
static NSString *const kClientID = #"***";
static NSString *const kClientSecret = #"***";
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName clientID:kClientID clientSecret:kClientSecret];
if ([auth refreshToken] != nil) {
[auth beginTokenFetchWithDelegate:self didFinishSelector:#selector(auth:finishedRefreshWithFetcher:error:)];
}
}
- (void)auth:(GTMOAuth2Authentication *)auth finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher error:(NSError *)error {
if (error != nil) {// Refresh failed
return;
}
[self retrieveEmails];
}
I can store token and just reuse it w/o refresh, i.e. just skip startAuth2 and go straight to retriveEmails and it all works fine too, but not sure if it is safe.
Thanks

Because you're calling completionHandler(UIBackgroundFetchResultNewData); (which tells that your background fetch is done) immediatly after [self startOAuth2]; and thus without waiting for your delegate to be called.
You need to find a way to call completionHandler(UIBackgroundFetchResultNewData); in the - (void)auth:(GTMOAuth2Authentication *)auth finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher error:(NSError *)error method.

Related

google play game check for user is sign in or not

i have implemented google play games in my app for leaderboard.
my requirement is to display info Alert when user login first time in safari(redirect to safari automatically for login)
i have written this code
[GPGManager sharedInstance].statusDelegate = self;
BOOL isSignedIn = [[GPGManager sharedInstance] signInWithClientID:kGoogleClient silently:flag];
NSLOG(#“is signedin %d",[GPGManager sharedInstance].isSignedIn);
but every time [GPGManager sharedInstance].isSignedIn this variable returns false.
any one have any idea ??
You must configure the GGLContext shared instance. You can do this in many places in your app. Often the easiest place to configure this instance is in your app delegate's application:didFinishLaunchingWithOptions: method.
In your app delegate's .h file, declare that this class implements the GIDSignInDelegate protocol.
#import <Google/SignIn.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, GIDSignInDelegate>
AppDelegate.h
In your app delegate's application:didFinishLaunchingWithOptions: method, configure the GGLContext shared instance and set the sign-in delegate.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSError* configureError;
[[GGLContext sharedInstance] configureWithError: &configureError];
NSAssert(!configureError, #"Error configuring Google services: %#", configureError);
[GIDSignIn sharedInstance].delegate = self;
return YES;
}
Implement the application:openURL:options: method of your app delegate. The method should call the handleURL method of the GIDSignIn instance, which will properly handle the URL that your application receives at the end of the authentication process.
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options {
return [[GIDSignIn sharedInstance] handleURL:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]];
}
In the app delegate, implement the GIDSignInDelegate protocol to handle the sign-in process by defining the following methods:
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
// Perform any operations on signed in user here.
NSString *userId = user.userID; // For client-side use only!
NSString *idToken = user.authentication.idToken; // Safe to send to the server
NSString *fullName = user.profile.name;
NSString *givenName = user.profile.givenName;
NSString *familyName = user.profile.familyName;
NSString *email = user.profile.email;
// ...
}
- (void)signIn:(GIDSignIn *)signIn
didDisconnectWithUser:(GIDGoogleUser *)user
withError:(NSError *)error {
// Perform any operations when the user disconnects from app here.
// ...
}

Can we add or use handleOpenURL other app delegate

I'm building SDK for my product and there will be no app delegate class. I used Dropbox Integration in my SDK, so I'm bound to use following method.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSString *stringUrl = [url absoluteString];
if ([stringUrl containsString:#"cancel"]) {
// Handle if user cancelled the login
[[NSNotificationCenter defaultCenter] postNotificationName:#"dropboxRegistrationCancel" object:self];
return NO;
}
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
NSLog(#"App linked successfully!");
// At this point you can start making API calls
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationComeBackFromDropBoxLoginPage object:self];
}
return YES;
}
// Add whatever other url handling code your app requires here
return NO;
}
So Issue is there is no app delegate than how can use above method?
Or any alternate solution for this to use this in another class (like to inherit UIResponder class).
Looking for help.
If you are building a SDK with no appDelegate, then well, you have nothing to do. It will be in your project where you will be using your sdk that you will have to add this line.
By the way, the line NSString *stringUrl = [url absoluteString]; is absolutly useless since you are not using stringUrl later on, and the
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
// NSString *stringUrl = [url absoluteString];
return YES;
}
just means: yes you can quit my app to open another app when the user click on a link.
No biggy....

FBSDKSharingDelegate callback not working, even when post works fine

FBSDKSharingDelegate callback is not working. I can post to facebook fine with the ios SDK, but I'd like to detect if the post was successful and take additional action to notify the user. However, the callback is not working for me. The delegate methods are not being called and I don't know why.
Using ios8, Parse as my backend. In Parse, the user is linked to FB. I'm using the IOS simulator.
What I've tried:
I've ensured that publish permissions are granted, saved, and linked to the Parse user. I've run a check and "publish_actions" are detected OK. The posting works fine as I can see the post on the facebook account. It's just the callback that is not working. I've checked my fb setup and it looks fine. For good measure at the very bottom I've included that relevant code from my app delegate. I've blocked out confidential keys with XXXX.
Code:
1st: See if user is logged in to Parse, if not, send to sign in and link to facebook account. Once that is done, I request "publish" permissions and link that additional permission to the Parse user. I know this works b/c when I recompile, it remembers the "publish" permissions and goes right to into the post.
#interface FacebookAPIPost () <FBSDKSharingDelegate>
#end
#implementation FacebookAPIPost
-(void)shareSegmentFacebookAPI { //if statement below
//1) logged in?, if not send to sign up screen
//2) else if logged in, link account to facebook account, then send post
//3) else send to post b/c signed up and linked already.
PFUser *currentUser = [PFUser currentUser];
if(!currentUser) {
[self pushToSignIn];
} else if(![PFFacebookUtils isLinkedWithUser:currentUser]){
[self linkUserToFacebook:currentUser];
NSLog(#"user account not linked to facebook");
} else {
[self shareSegmentWithFacebookComposer];
}
}
-(void)linkUserToFacebook:currentUser{
[PFFacebookUtils linkUserInBackground:currentUser withPublishPermissions:#[#"publish_actions"] block:^(BOOL succeeded, NSError *error) {
if(error){
NSLog(#"There was an issue linking your facebook account. Please try again.");
}
else {
NSLog(#"facebook account is linked");
//Send the facebook status update
[self shareSegmentWithFacebookComposer];
}
}];
}
-(void)shareSegmentWithFacebookComposer{
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"]) {
[self publishFBPost]; //publish
} else {
NSLog(#"no publish permissions"); // no publish permissions so get them, then post
[PFFacebookUtils linkUserInBackground:[PFUser currentUser]
withPublishPermissions:#[ #"publish_actions"]
block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"User now has read and publish permissions!");
[self publishFBPost];
}
}];
Here is where the post gets made:
-(void) publishFBPost{
FBSDKShareLinkContent *content = [FBSDKShareLinkContent new];
content.contentURL = [NSURL URLWithString:[self.selectedSegment valueForKey:#"linkToContent"]];
content.contentTitle = [self.selectedProgram valueForKey:#"programTitle"];
content.contentDescription = [self.selectedSegment valueForKey:#"purposeSummary"];
PFFile *theImage = [self.selectedSegment valueForKey:#"segmentImage"];
NSString *urlString = theImage.url;
NSURL *url = [NSURL URLWithString:urlString];
content.imageURL = url;
FBSDKShareDialog *shareDialog = [FBSDKShareDialog new];
[shareDialog setMode:FBSDKShareDialogModeAutomatic];
// [FBSDKShareDialog showFromViewController:self.messageTableViewController withContent:content delegate:self];
[shareDialog setShareContent:content];
[shareDialog setDelegate:self];
[shareDialog setFromViewController:self.messageTableViewController];
[shareDialog show];
}
Delegate methods below are not working. Meaning after the post is complete, I can see it on the FB account, but none of these delegate methods execute.
#pragma mark - delegate methods
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results {
// if ([sharer isEqual:self.shareDialog]) {
NSLog(#"I'm going to go crazy if this doesn't work.%#",results);
// Your delegate code
// }
}
- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NSLog(#"sharing error:%#", error);
NSString *message = error.userInfo[FBSDKErrorLocalizedDescriptionKey] ?:
#"There was a problem sharing, please try again later.";
NSString *title = error.userInfo[FBSDKErrorLocalizedTitleKey] ?: #"Oops!";
[[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
- (void)sharerDidCancel:(id<FBSDKSharing>)sharer
{
NSLog(#"share cancelled");
}
Console output:
The only message I get back after posting is after a few seconds this message appears:
plugin com.apple.share.Facebook.post invalidated
Please help!
Footnote: appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Parse.
[Parse enableLocalDatastore];
[Parse setApplicationId:#"XXXX"
clientKey:#"XXX"];
[PFFacebookUtils initializeFacebookWithApplicationLaunchOptions:launchOptions];
//Initialize Facebook
[FBSDKAppEvents activateApp];
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
//Method added for facebook integration
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[FBSDKAppEvents activateApp];
}
I think I am too late answering this question but someone else could trap into this as well, that's why sharing my knowledge.
As per Facebook API documentation
sharer:didCompleteWithResults: Sent to the delegate when the share completes without error or cancellation.
The results from the sharer. This may be nil or empty.
its probably because this delegate method is only get called when the post is successfully shared. In case of failure the other delegate method sharer:didFailWithError:get called. I think Facebook API should not need to add the result parameter is that case.
So in my experience if sharer:didCompleteWithResults whenever this is called that would mean success.

Handoff icon not disappearing from the phone

I create a handoff request for my apple watch app with the following code:
[self updateUserActivity:#"..." userInfo:selectedTopic webpageURL:[NSURL URLWithString:nil]];
And I handle the request in the phone AppDelegate with the following code:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
BOOL handled = NO;
[userActivity becomeCurrent];
NSString *type = [userActivity activityType];
NSDictionary *userInfo = [userActivity userInfo];
if ([type isEqualToString:#"..."]) {
//does action
[userActivity invalidate];
handled = YES;
}
return handled;
}
The issue is the handoff icon does not go away even though this code is executed. What am I doing wrong here? Why will the handoff icon not disappear?
I've seen this behaviour on other applications. The only applications I see with the correct behaviour are native apps.

Facebook SDK on iOS App Session Fixing

I'm trying to authenticate users through facebook. I've read the SDK and done every necessary things that need to be done, BUT if I try to Login into my app while LoggedIn in this place,
It Tells me SESSION CLOSED but if I logged out and try to SIgnIn into my app, it checks if I've the facebook app and try to check if logged in if I'm, it updates and do what I want it to do, but if I'm not, it brings a Dialog for me to login. But, once I sign into the Image above, the other thing happens CLOSED SESSION ERROR. Would appreciate the help, thanks.
My Code Below
//For the button
- (IBAction)facebook:(id)sender {
// If the session state is any of the two "open" states when the button is clicked
if (FBSession.activeSession.state == FBSessionStateOpen || FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {
[FBSession.activeSession closeAndClearTokenInformation];
} else {
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile",#"user_friends"]
allowLoginUI:YES
completionHandler: ^(FBSession *session, FBSessionState state, NSError *error) {
// Retrieve the app delegate
appDelegate = [UIApplication sharedApplication].delegate;
// Call the app delegate's sessionStateChanged:state:error method to handle session state changes
[appDelegate sessionStateChanged:session state:state error:error];
if (state == FBSessionStateOpen) {
[self fetchUserDetails];
UINavigationController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
}];
}
}
//AppDelegate
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
//return [FBAppCall handleOpenURL:url sourceApplication:annotation];
return [FBSession.activeSession handleOpenURL:url];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Handle the user leaving the app while the Facebook login dialog is being shown
// For example: when the user presses the iOS "home" button while the login dialog is active
[FBAppCall handleDidBecomeActive];
}
// This method will handle ALL the session state changes in the app
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
// If the session was opened successfully
if (!error && state == FBSessionStateOpen){
NSLog(#"Session opened");
// Show the user the logged-in UI
return;
}
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
// If the session is closed
NSLog(#"Session closed");
// Show the user the logged-out UI
}
// Handle errors
if (error){
NSLog(#"Error");
NSString *alertText;
NSString *alertTitle;
// If the error requires people using an app to make an action outside of the app in order to recover
if ([FBErrorUtility shouldNotifyUserForError:error] == YES){
alertTitle = #"Something went wrong";
alertText = [FBErrorUtility userMessageForError:error];
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
} else {
// If the user cancelled login, do nothing
if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
NSLog(#"User cancelled login");
// Handle session closures that happen outside of the app
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession){
alertTitle = #"Session Error";
alertText = #"Your current session is no longer valid. Please log in again.";
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
// Here we will handle all other errors with a generic error message.
// We recommend you check our Handling Errors guide for more information
// https://developers.facebook.com/docs/ios/errors/
} else {
//Get more error information from the error
NSDictionary *errorInformation = [[[error.userInfo objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"] objectForKey:#"body"] objectForKey:#"error"];
// Show the user an error message
alertTitle = #"Something went wrong";
alertText = [NSString stringWithFormat:#"Please retry. \n\n If the problem persists contact us and mention this error code: %#", [errorInformation objectForKey:#"message"]];
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
}
}
// Clear this token
[FBSession.activeSession closeAndClearTokenInformation];
// Show the user the logged-out UI
}
}
I add a similar problem, in my case I found the reason, I was using an application id that was not correct (because of test environment/production environment configuration of my app, I was developing and I had no facebook app for the development application id).
I'm sharing my approach for debugging the problem:
just launch the app from xcode, put a break point where the error is intercepted (CordovaFacebook.m) and there if you are lucky you should see a better description of the problem
Furthermore, remember to set the correct fb app id inside .plist
remember also that if you are using a test application on facebook, that application should be either publicly available or you should set some test users inside the ROLES tab of the admin section
Another source of interesting debugging informations is the following:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
if (!url) {
return NO;
}
set a break point there and inspect the url variable content.

Resources