Native iOS Facebook SSO won't return to app - objective-c

Ok this may seem like a duplicate but I've been through all of the questions that center around the issue and I still can't seem to get this to work for my app.
I have followed the iOS tutorial on the Facebook Developer site and I have done the following:
Created a Facebook App to get the Facebook App ID.
I've assigned this App ID to a constant and I use it to initialize the Facebook object within my app.
I've made sure my app is flagged for multitasking
I've setup the fbAPP_ID in the plist under the URLs drop down
I've made my AppDelegate a FBSessionDelegate and implemented very simple fbDidLogin and fbDidNotLogin delegate methods.
Versions: XCode 4.3.2 on Lion, iOS 5.1, Facebook iOS 4.1.1
All of this and I still can't seem to get Facebook to return control back to my app when it authorizes. It just stays in Facebook instead.
Code: (Note FB_APP_ID changed to basic number for viewing here)
AppDelegate.h:
#import <UIKit/UIKit.h>
#import "FBConnect.h"
#define FB_APP_ID #"1234567890"
#interface AppDelegate : UIResponder <UIApplicationDelegate, FBSessionDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong) Facebook* facebook;
#end
AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize facebook = _facebook;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_facebook = [[Facebook alloc] initWithAppId:FB_APP_ID andDelegate:self];
if (![_facebook isSessionValid])
{
[_facebook authorize:nil];
}
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL *)url
{
return [_facebook handleOpenURL:url];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [_facebook handleOpenURL:url];
}
- (void)fbDidLogin
{
NSLog(#"App logged in");
}
- (void)fbDidNotLogin:(BOOL)cancelled
{
NSLog(#"App did not login");
}
#end
Relevant Info.plist porton:
Facebook App screen (mostly pinked out):
Note: I tried it with only basic settings and also with the iOS Native app settings with a fake iTunes store ID but the same bundle ID from my provisioning profile.
No matter what I do it always loads up Facebook and asks to authorize my app and then it simply closes the auth screen window and sits there on my Facebook screen instead of returning to my app.
Also note: I know it will always ask because I'm not checking for the auth token and expiration date.....that's the next step....for now I want to at least get it to return to my app before I worry about that.
Have I missed something here? Anything else I can try to get Facebook to return to my app?
EDIT: Also when debugging, I can't seem to find any spot where either openURL is called for some reason.
EDIT #2: When running it on the simulator, it loads up the FB Dialog via Safari (as expected) and then when I authorize, safari throws an error as if the URL they are trying to use to get back to my app is invalid:
This still doesn't tell me much since my fb1234567890 is setup right as far as I know in the Info.plist.

Your info.plist seems to be formatted incorrectly
Ensure that the url scheme is nested under an item (an array to be exact) called "URL Schemes"
This array should contain all possible URL Schemes that can launch your app
See the attached image for a better explanation of URL Schemes
Also, the "URL identifier" should be your bundle identifier: ex. com.mycompany.myapp
This should match the identifier you provide on your provisioning profiles
hope this helps!

All you have to follow these steps
STEP 1:
Facebook developer side stuff:
If your app is already submitted then fill iphone/ipad appstore id otherwise leave it empty.
STEP 2:
Next in your XCode:
Goto
Target > Info > URL Types > click plus icon and add the items like this-
In my application, there were both options to login via facebook and google so I created two URL Types. You can see second one are Facebook URL Type settings. URL Scheme will be your facebook app id from facebook Developer account with fb at starts. So if your facebook developer app id is 9876543267575645 then URL Scheme will become fb9876543267575645.
STEP 3:
Next, you have to add a new key in info.plist. So remain in same screen and add FacebookAppID like this screen:
All this stuff will bring your app back after successful facebook oauth.
STEP 4:
Rest you will do in your AppDelegate class. Simply add this method in AppDelegate.m
NSString *retURL = [NSString stringWithFormat:#"%#", url];
if ([retURL rangeOfString:facebookid].location != NSNotFound)
{
// if your application is coming back from facebook native app.
return [_facebook handleOpenURL:url];
}
else
{
// if your app is coming back from google plus oauth.
return [GPPURLHandler handleURL:url sourceApplication:sourceApplication annotation:annotation];
}
during all this, must intialise and synthesize facebook instance in AppDelegate.m
#synthesize facebook = _facebook;
and didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_facebook = [[Facebook alloc] initWithAppId:facebookid];
// rest of your code is already here......
}
One more important thing. During I was implementing these steps, I also did too much research to achieve this and finally the working stuff is here. I made these settings in two of my apps.
But while I was creating third app, I started from beginning and did not follow STEP 1 and STEP 3 and voila.... redirection works! So i don't think facebook developer account Native iOS App settings are necessary while you are making Facebook login in your app..

I ran into another issue. I edited the raw Info.plist file and added:
<key>URL Types</key>
<array>
<dict>
<key>URL Schemes</key>
<array>
<string>fbxxxxxxxxx</string>
</array>
</dict>
</array>
That didn't work because the keys are not 'URL Types', 'URL Schemes' and 'URL Identifier', it's 'CFBundleURLTypes' , 'CFBundleURLSchemes' and 'CFBundleURLIdentifier' instead. Xcode will rename that in the editor to 'URL Types', 'URL Schemes' and 'URL Identifier'.
To be sure select the Info.plist in xcode, right click->show raw keys/values.

None of the above, or other answers on SO, worked for me, though I seemed to have the exact same issue.
However, when I enabled "Deep Linking", it worked perfectly, so it may have been a combination of all of the above AND enabling that option too.

Related

FBSDK 4 iOS catch user logout

I'm writing this app, that allows a user to login to my service either by registering first to my service, or use their Facebook, Instagram, etc login.
Now, if for some reason, a user has logged in to my app with FB, but decides to click the FB 'Logout' button, I want to offer the opportunity to login alternatively. For that I need to catch the Facebook Logout action.
So, the question is: how to trigger my "-(void)loginAlternatively" method? Where can I find a hook for that?
I tried to add
if (![FBSDKAccessToken currentAccessToken]) {
NSLog(#"Logged out of facebook");
}
to my AppDelegate's
- (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
since that is where I handle the post-login FB stuff. However, that doesn't seem to be the place.
It is just a matter of reading the documentation...
So:
Make the viewController a FBSDKLoginButtonDelegate
Add the *- (void)loginButtonDidLogOut:(FBSDKLoginButton )loginButton method to your .m file
Connect the button's delegate to the viewcontroller in the storyboard. (or declare myFBButton.delegate= self.)
Hope this helps others

Facebook Audience Network interstitials not showing

I am working on my app, which is built using Sprite Kit. I tried to add the function to load the ad, but the ad is not loading. I am not sure whether this is because my app is not live yet, and I need to turn on some sort of 'sandboxing', or whether I am doing something wrong. The point of display of the ad is called from an SKScene, and the ad display functions and methods (the loading, displaying, as well as the delegate) is in the parent UIViewController.
Below is the code for directing the app to the ViewController from the SKScene:
GameViewController *vc = (GameViewController*)self.view.window.rootViewController;
[vc loadInterstitial];
GameViewController is the parent view controller of the SKScene. Below is the code contained in the GameViewController:
- (void)loadInterstitial
{
self.interstitialAd =
[[FBInterstitialAd alloc] initWithPlacementID:#"Placement ID"];
self.interstitialAd.delegate = self;
[self.interstitialAd loadAd];
}
- (void)interstitialAdDidLoad:(FBInterstitialAd *)interstitialAd
{
NSLog(#"Interstitial ad is loaded and ready to be displayed");
// You can now display the full screen ad using this code:
[interstitialAd showAdFromRootViewController:self];
}
- (void)interstitialAd:(FBInterstitialAd *)interstitialAd
didFailWithError:(NSError *)error
{
NSLog(#"Interstitial ad is failed to load with error: %#", error);
}
The method interstitialAdDidLoad is not being called, nor am I receiving any errors.
I faced the issue of interstitial ads not showing up when I declared FBInterstitialAd instance inside loadInterstitial method. After moving the declaration outside the method, It magically worked. See the Facebook Audience network samples here: https://github.com/fbsamples/audience-network-support
Add FBInterstitialAdDelegate to your GameViewController interface.

iCloud enabled - Stop the open file displaying on application launch?

I've just added iCloud support to an app that I am working on. Its working great, except that when I open the application without a document in focus the iCloud open file dialog appears and I don't want it to!
In my app delegate I have:
- (BOOL) applicationShouldOpenUntitledFile:(NSApplication *)sender
{
[mainWindowController.window makeKeyAndOrderFront:self];
return NO;
}
Which I use to show my own custom window. However now, both the iCloud open file dialog and my own dialog are displayed. Any ideas on how I can get rid of the iCloud dialog?
https://developer.apple.com/library/prerelease/content/releasenotes/AppKit/RN-AppKitOlderNotes/index.html
NSDocument Support for iCloud
In 10.8, NSDocument-based applications with a ubiquity-container-identifiers entitlement gain new functionality and UI to facilitate iCloud document management.
When iCloud is enabled and an application is first launched or re-activated and no windows are visible or being restored, instead of creating a new Untitled document, NSDocumentController will display a non-modal open panel showing the user's iCloud library.
...
Applications that do not wish to use these features for any or all of their NSDocument subclasses can override +[NSDocument usesUbiquitousStorage] and return NO. If all of the application's declared NSDocument subclasses return NO from this method, then NSDocumentController will never show the new non-modal open panel.
So if you can give up using the features listed in this release note, return NO at +[NSDocument usesUbiquitousStorage].
I confirmed you can still open/save your file into iCloud storage from the normal dialog.
Putting below codes in your App Delegate lets you bypass that iCloud pop up New Document screen. Tested for High Sierra.
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Schedule "Checking whether document exists." into next UI Loop.
// Because document is not restored yet.
// So we don't know what do we have to create new one.
// Opened document can be identified here. (double click document file)
NSInvocationOperation* op = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(openNewDocumentIfNeeded) object:nil];
[[NSOperationQueue mainQueue] addOperation: op];
}
-(void)openNewDocumentIfNeeded
{
NSUInteger documentCount = [[[NSDocumentController sharedDocumentController] documents]count];
// Open an untitled document what if there is no document. (restored, opened).
if(documentCount == 0){
[[NSDocumentController sharedDocumentController]openUntitledDocumentAndDisplay:YES error: nil];
}
}
- (BOOL) applicationShouldOpenUntitledFile:(NSApplication *)sender
{
[mainWindowController.window makeKeyAndOrderFront:self];
return NO;
}
This part is correct. I've just tested it.
Just make sure your that this class is really your app delegate.
Make a new class called prefixAppDelegate
In your MainMenu.xib, drag a new object to the side and set it's custom class to the app delegate class
Right click Application and drag from Delegate down to your app delegate object.
Now just paste the code above into your app delegate class
If that still doesn't help, try logging something in applicationShouldOpenUntitledFile:.
Also, I recommend not to set [mainWindowController.window makeKeyAndOrderFront:self]; in this method. You should rather use the app delegate method applicationDidFinishLaunching: method.
My observation and fix:
[applicationShouldOpenUntitledFile:] won't be executed except you remove Key NSDocumentClass from *-info.plist. But this is harmful if your app is document based application, it won't open the document type you linked.
My fix is open my customised window directly in -(void)applicationWillFinishLaunching:(NSNotification *)notification method (Application delegate)
ETDocumentWindowController *windowController = (ETDocumentWindowController*)get your own window controller here...;
[windowController.window makeKeyAndOrderFront:nil];
I thought I would share my solution to this issue as I see others still looking for an answer. Its not a great solution but it does the trick.
Subclass NSDocumentController and add the following:
+ (void) setCanOpenUntitledDocument: (BOOL) _canOpenUntitledDocument
{
canOpenUntitledDocument = _canOpenUntitledDocument;
} // End of setCanOpenUntitledDocument:
- (void) openDocument: (id) sender
{
// With iCloud enabled, the app keeps trying to run openDocument: on first launch (before apphasfinishedlaunching gets set.
// This method lets us check and see if the app has finished launching or not. If we try to open a document before
// its finished, then don't let it.
if(!canOpenUntitledDocument)
{
return;
} // End of appHasFinishedLaunching not set
[super openDocument: sender];
} // End of openDocument:
Add the following to your app delegate:
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
// Finished launching. Let us open untitled documents.
[SQLProDocumentController setCanOpenUntitledDocument: true];
...
}
And the reasoning -- By setting a breakpoint in openDocument I've found that its called before applicationDidFinishLaunching, applicationShouldOpenUntitledFile or applicationShouldHandleReopen:hasVisibleWindows: get called, meaning adding those methods is useless. Again, it's not great code but it works and does the trick. (None of the other solutions have worked for me).
I ran into a similar problem -- it turned out that in my case, I had to remove the NSDocumentClass key and value from my Info.plist in the CFBundleDocumentTypes array. Only then would the applicationShouldOpenUntitledFile: method get called and thus allow me to prevent the iCloud/Document window from opening.

How can I use a "Splash" UIViewController to conditionally redirect to another view controller?

I have a "Splash screen" ViewController extending UIViewController, set as the the initial app VC in my storyboard. This controller features a login form.
When the app starts, and before anything is displayed on the screen, I want this splash VC to check the user defaults to see if the user is already logged in. If so, I want the splash VC to redirect to the app's home VC, all before anything is displayed on the screen.
If the user is not logged in, I want the Splash VC to finish loading, displaying the login forms.
How would I go about implementing this? Would I place all of these checks in the init methods? I was having a hard time getting any code in the splash VC init methods to run at all, for some reason these methods don't get called.
Code in the viewDidLoad method runs fine, but running the code there would defeat the purpose of trying to allow the already-logged-in-user to start the app right into the home screen.
Suggestions? Thanks in advance.
My pick of the place to put this logic is in application didFinishLaunchingWithOptions: of application delegate. Here is how it would look :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//////////////////////////////////////////////
// 1. do loading data etc.
// 2. check whether user is signed in or not
//////////////////////////////////////////////
if(already signed in)
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.window.rootViewController performSegueWithIdentifier:#"segue identifier to home VC" sender:self.window.rootViewController];
});
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.window.rootViewController performSegueWithIdentifier:#"segue identifier to login VC" sender:self.window.rootViewController];
});
}
});
return YES;
}
And this is my quick storyboarding to assist the code. Hopefully you get the idea.

applicationDidFinishLauching never invoked, WillFinishLauching does

I have a Cocoa application which works fine except it will not invoke applicationDidFinishLauching on my app delegate. applicationWillFinishLauching does work though.
In IB I have set the delegate from "Application" (and also File's owner) to my "XXX App Delegate" object. All other application specific methods are called correctly weirdly enough.
What could I be doing wrong; I have no idea where to search anymore
My code:
#interface NZBVortexAppDelegate : NSObject
{
NSWindow *window;
NZBqueue *connectionPool;
MainWindowViewController *mainWindowViewController;
}
#property (assign) IBOutlet NSWindow *window;
#end
.m file
#implementation NZBVortexAppDelegate
#synthesize window;
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSLog(#"Not invoked");
}
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
NSLog(#"Invoked");
}
#end
Can you give me some pointers? I even stepped from WillFinishLauching (step in and over) but do not seem to hit any of my code anymore, can't debug more from within the frameworks.
If you are building a Mac OS X application then this is indeed strange. If, on the other hand you are working on an iOS app then this is expected as for some unknown reason Apple changed the method every so slightly. In OS X your method above is exactly correct for iOS where you have NSNotification * in applicationDidFinishLaunching: it should be UIApplication *. I have no idea why they did this as applicationWillFinishLaunching: is still an NSNotification.
I got the answer, it seems my preferences plist was a bit corrupt causing Sparkle SUUpdater object never to finish checking a update during startup and in turn blocking the event being called. Took me a few hours to figure out because sparkle doesn't log this issue at least.
I've contacted the Sparkle developer to add a little more sanity checking.
Thanks guys!