SafariViewController: How to grab OAuth token from URL? - swift

Trying to use Facebook OAuth with the SafariViewController. First I open the authURL with SafariViewController, which if the user is logged in to Facebook on Safari, will redirect them and return an OAuth URL with the token for that specific service, e.g. Instagram
RESPONSE: https://www.facebook.com/connect/login_success.html#access_token=BLAHTOKENRESPONSE&expires_in=5114338
When SafariViewController has redirected I want to grab the response URL and store it so I can grab the token. Here is my code:
import SafariServices
let kSafariViewControllerCloseNotification = "kSafariViewControllerCloseNotification"
import UIKit
// facebook OAuth URL for service
let authURL = NSURL(string: "https://www.facebook.com/dialog/oauth?client_id=3627644767&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token")
class ViewController: UIViewController, SFSafariViewControllerDelegate {
var safariVC: SFSafariViewController?
#IBOutlet weak var loginButton: UIButton!
#IBAction func loginButtonTapped(sender: UIButton) {
safariVC = SFSafariViewController(URL: authURL!)
safariVC!.delegate = self
self.presentViewController(safariVC!, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// not firing the safariLogin function below
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.safariLogin(_:)), name: kSafariViewControllerCloseNotification, object: nil)
}
func safariLogin(notification: NSNotification) {
print("Safari Login call")
// get the url form the auth callback
let url = notification.object as! NSURL
print(url)
self.safariVC!.dismissViewControllerAnimated(true, completion: nil)
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
print("application call")
// just making sure we send the notification when the URL is opened in SFSafariViewController
if (sourceApplication == "com.application.SafariViewTest") {
NSNotificationCenter.defaultCenter().postNotificationName(kSafariViewControllerCloseNotification, object: url)
return true
}
return true
}
}
It opens the authURL and redirects to the correct response URL, but the observer does not fire the safariLogin function to grab the URL. Any help would be much appreciated!
Thanks so much!

iOS 12+
iOS 12 Beta already deprecates SFAuthenticationSession (see below) in favor of ASWebAuthenticationSession. It looks like it is used exactly the same way but requires the new AuthenticationServices framework.
iOS 11
iOS 11 introduced SFAuthenticationSession which is so much easier to handle. Given its nature this beta API may still change but there already are a couple of examples (1, 2) on the internet. First, you need a completion handler that is called with the result of the authentication request:
let completion : SFAuthenticationSession.CompletionHandler = { (callBack:URL?, error:Error?) in
guard error == nil, let successURL = callBack else {
return
}
let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "oauth_token"}).first
// Do what you have to do...
}
Then you simply create a SFAuthenticationSession and start it.
let authURL = "https://the.service.you/want/toAuthorizeWith?..."
let scheme = "YOURSCHEME://"
let authSession = SFAuthenticationSession(url: authURL, callbackURLScheme: scheme, completionHandler: completion)
authSession.start()
iOS 10 and before
As some have noted in the comments, the accepted answer is incomplete and won't work on its own. When strolling through Strawberry Code's blog post one can find a link to the related GitHub project. That project's README.MD explains a crucial part of the setup, namely adding the redirect URI to Info.plist. So the whole thing goes down like this:
AppDelegate.swift
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let sourceApplication = options[.sourceApplication] {
if (String(describing: sourceApplication) == "com.apple.SafariViewService") {
NotificationCenter.default.post(name: Notification.Name("CallbackNotification"), object: url)
return true
}
}
return false
}
ViewController.swift
Register for the notification to call your handler in some sensible place. I'd recommend not doing it in viewDidLoad() but only before you are actually presenting the SFSafariViewController.
NotificationCenter.default.addObserver(self, selector: #selector(safariLogin(_:)), name: Notification.Name("CallbackNotification"), object: nil)
let safariVC = SFSafariViewController(URL: authURL)
safariVC.delegate = self
self.present(safariVC, animated: true, completion: nil)
And then remove the observance in the handler:
#objc func safariLogin(_ notification : Notification) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)
guard let url = notification.object as? URL else {
return
}
// Parse url ...
}
Remember that the user is able to dismiss the SFSafariViewController by tapping the Done button, so be sure to adopt the SFSafariViewControllerDelegate protocol and remove the observance like this as well:
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("CallbackNotification"), object: nil)
}
Info.plist
To make it all work you need to add your redirect URI scheme to Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.YOUR.BUNDLE.IDENTIFIER</string>
<key>CFBundleURLSchemes</key>
<array>
<string>YOURSCHEME</string>
</array>
</dict>
</array>
Of course YOURSCHEME has to match the scheme of the redirect URI you registered with the web service you're trying to oAuthorizing with.

Figured it out. Some of the methods were pre iOS 9 and now deprecated. I also had the application function in the ViewController I created when it should have been defined in the AppDelagate.swift. For example
Added at end of AppDelegate.swift
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
print("app: \(app)")
// print OAuth response URL
print("url: \(url)")
print("options: \(options)")
if let sourceApplication = options["UIApplicationOpenURLOptionsSourceApplicationKey"] {
if (String(sourceApplication) == "com.testApp.Incognito") {
NSNotificationCenter.defaultCenter().postNotificationName(kSafariViewControllerCloseNotification, object: url)
return true
}
}
return true
}
ViewController.swift
import SafariServices
let kSafariViewControllerCloseNotification = "kSafariViewControllerCloseNotification"
// facebook OAuth URL
let authURL = NSURL(string: "https://www.facebook.com/dialog/oauth?client_id=3627644767&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token")
class ViewController: UIViewController, SFSafariViewControllerDelegate {
var safariVC: SFSafariViewController?
#IBOutlet weak var loginButton: UIButton!
#IBAction func loginButtonTapped(sender: AnyObject) {
safariVC = SFSafariViewController(URL: authURL!)
safariVC!.delegate = self
self.presentViewController(safariVC!, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.safariLogin(_:)), name: kSafariViewControllerCloseNotification, object: nil)
}
func safariLogin(notification: NSNotification) {
// get the url from the auth callback
let url = notification.object as! NSURL
// Finally dismiss the Safari View Controller with:
self.safariVC!.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func safariViewControllerDidFinish(controller: SFSafariViewController) {
controller.dismissViewControllerAnimated(true) { () -> Void in
print("You just dismissed the login view.")
}
}
func safariViewController(controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
print("didLoadSuccessfully: \(didLoadSuccessfully)")
}
}

Related

About the callback of SKStoreReviewController.requestReview()

If the review popup initiated from a view controller shows up, there isn't a way to switch the window focus back to the view controller when the popup is dismissed due to lack of callback function of SKStoreReviewController.requestReview().
I would like to make a call to becomeFirstResponder() when the review popup is dismissed. Any idea?
Is there a way to extend the SKStoreReviewController and add a callback somehow?
Warning this will probably break at some point.
Step 1: add this code to your didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let windowClass: AnyClass = UIWindow.self
let originalSelector: Selector = #selector(setter: UIWindow.windowLevel)
let swizzledSelector: Selector = #selector(UIWindow.setWindowLevel_startMonitor(_:))
let originalMethod = class_getInstanceMethod(windowClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(windowClass, swizzledSelector)
let didAddMethod = class_addMethod(windowClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
if didAddMethod {
class_replaceMethod(windowClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
return true
}
Step 2: add this class
class MonitorObject: NSObject {
weak var owner: UIWindow?
init(_ owner: UIWindow?) {
super.init()
self.owner = owner
NotificationCenter.default.post(name: UIWindow.didBecomeVisibleNotification, object: self)
}
deinit {
NotificationCenter.default.post(name: UIWindow.didBecomeHiddenNotification, object: self)
}
}
Step 3: Add this UIWindow extension
private var monitorObjectKey = "monitorKey"
private var partialDescForStoreReviewWindow = "SKStore"
extension UIWindow {
// MARK: - Method Swizzling
#objc func setWindowLevel_startMonitor(_ level: Int) {
setWindowLevel_startMonitor(level)
if description.contains(partialDescForStoreReviewWindow) {
let monObj = MonitorObject(self)
objc_setAssociatedObject(self, &monitorObjectKey, monObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Step 4: add this to ViewDidLoad of your controller where you want this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeHiddenNotification(_:)), name: UIWindow.didBecomeHiddenNotification, object: nil)
}
Step 5: add the callback for the notification and check that the associated object is a match
#objc func windowDidBecomeHiddenNotification(_ notification: Notification?) {
if notification?.object is MonitorObject {
print("hello")
}
}
Now when a review dialog is closed the notification is triggered and 'print("hello") will be called.
Sometimes iOS app is losing the responder chain, like in the above example of showing StoreKit prompt. What we can do is to detect such events in UIApplication.sendAction and reactivate the first responder chain via becomeFirstResponder. UIKit will reestablish the first responder chain and we can resend the same event.
class MyApplication: UIApplication {
func reactivateResponderChainWhenFirstResponderEventWasNotHandled() {
becomeFirstResponder()
}
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
let wasHandled = super.sendAction(action, to: target, from: sender, for: event)
if wasHandled == false, target == nil {
reactivateResponderChainWhenFirstResponderEventWasNotHandled()
return super.sendAction(action, to: target, from: sender, for: event)
}
return wasHandled
}
}
This works for me on iOS 13 and does not require any private API access.

Game Center match is invalid always

I'm trying to learn how to build a Game-Center based app. its a turn based extremely simple game that just basically logs what button you press and sends it to he other player. I am having a very hard time implementing all the game center features since Apple's documentation has not been updated for Swift. I've been guessing at everything and reading off Objective-C examples and hoping for the best (somehow I've managed to get a few things going, altough I'm not sure if they are correct)
Anyways, I made a new iCloud account for my Simulator and ran the app on my phone and the simulator simultaneously, trying to get them to match up in a game. However I always get a "match request is invalid" error:
EDIT I have registered the app in iTunesConnect, I've implemented leaderboards and tested them and they work (So I assume the iTunesConnect thing is properly working and set-up)
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2
request.minPlayers = 1
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
extension HomeVC: GKGameCenterControllerDelegate
{
func authenticatePlayer()
{
//CALLED ON VIEWDIDAPPEAR
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil
{
self.present(view!, animated: true, completion: nil)
} else {
print("AUTHENTICATED!")
print(GKLocalPlayer.localPlayer().isAuthenticated)
}
}
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
Here's the matchmaking code. Note I can get the Game Center screen to appear and tell me "how many players" and that its missing a player and gives the choice to invite friends and all that
extension HomeVC: GKMatchmakerViewControllerDelegate {
func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
print("match was cancelled")
viewController.dismiss(animated: true, completion: nil)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
print("didFailwithError: \(error.localizedDescription)")
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
print("Match found, ID: \(match.description)")
let gameScreenVC = self.storyboard?.instantiateViewController(withIdentifier: "mainGame") as! ViewController
gameScreenVC.providesPresentationContextTransitionStyle = true
gameScreenVC.definesPresentationContext = true
gameScreenVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen
gameScreenVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
match.delegate = gameScreenVC
self.present(gameScreenVC, animated: true, completion: nil)
}
}
If anyone is having this issue I fixed it by just replacing the "minelayers and max-layers" with the same amount, so the code now looks like this:
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2 //min and max should be the same
request.minPlayers = 2 //apparently they need to be the same
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
apparently Game Center does not like a min.player count of 1, its invalid. but thats how I got it to stop giving me Invalid Match Request warnings

SFSafariController loads URL fine in simulator but leaves a blank screen on device

So I'm using SFSafariController to load up an URL downloaded from cloudkit, like so.
if let yelpurl = detail.value(forKey: "Yelp") as? String
{
let url = URL(string: (yelpurl))
let safari = SFSafariViewController(url: url!, entersReaderIfAvailable: false)
safari.delegate = self as? SFSafariViewControllerDelegate
self.present(safari, animated: true, completion: {
})
} else {
}
}
The code itself is fine - no errors, pulls up the yelp page URL and loads it up in an SFSafariController in simulator, but on my device, I am left with a white blank screen. What is causing this disparity and how can I correct this on device?
To add, the plist still stands without any modification.
Edit 1:
I created a new project to purely test the below mentioned code to possibly discover a new error. This is what I found - and wondering how to solve.
Update:
I updated to 10.3.3 and deleted my browsing history.
Just created a sample project and it works on both simulator and iPhone 6S iOS 10.3.2, code:
import UIKit
import SafariServices
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
self.showLinksClicked()
}
}
func showLinksClicked() {
let yelpurl = "https://google.com"
let url = URL(string: (yelpurl))
let safari = SFSafariViewController(url: url!, entersReaderIfAvailable: false)
safari.delegate = self as? SFSafariViewControllerDelegate
self.present(safari, animated: true, completion: {
})
}
}
So looks like something is wrong with your URL or with way you call the method to present safari. Put your code to a separate method and be sure that you pass right URL.
On my app I did this:
import UIKit
import SafariServices
class ViewController: UIViewController, SFSafariViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
}
func linkIsClicked(){
let safariVC = SFSafariViewController(url: NSURL(string: "your url")! as URL)
self.present(safariVC, animated: true, completion: nil)
safariVC.delegate = self
}
And it perfectly works

Open UIWebView Links In Safari

I have an iOS app using Swift 3. I have a uiwebview in this app. Whenever a user clicks on a link in the webview, it opens up inside the webview. I need it to where the links open up inside the Safari app on the iPhone. Here's the code:
class VideosViewController: UIViewController {
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
var URL = NSURL(string: "http://example.com")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Can you please give me some code that I can enter? I'm new at coding, so thanks a lot.
UPDATE: Thanks to an answer I got some code, but the links are still not opening in Safari. Here's the code so far...
class VideosViewController : UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView : UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
let request = URLRequest(url: url)
webView.loadRequest(request)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked {
guard let url = request.url else { return true }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return false
}
return true
}
}
Using UIWebView:
Make sure your view controller conforms to UIWebViewDelegate and then implement this method in the controller:
class VideosViewController : UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView : UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
webView.delegate = self
let request = URLRequest(url: url)
webView.loadRequest(request)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked {
guard let url = request.url else { return true }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return false
}
return true
}
}
Using WKWebView:
Use a WKWebView instead of a UIWebView and make sure your controller conforms to the WKNavigationDelegate protocol. So your implementation would look something like the following:
class VideosViewController : UIViewController, WKNavigationDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "http://example.com") else { return }
webView = WKWebView(frame: self.view.frame)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.isUserInteractionEnabled = true
webView.navigationDelegate = self
self.view.addSubview(self.webView)
let request = URLRequest(url: url)
webView.load(request)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Check if a link was clicked
if navigationAction.navigationType == .linkActivated {
// Verify the url
guard let url = navigationAction.request.url else { return }
let shared = UIApplication.shared
// Check if opening in Safari is allowd
if shared.canOpenURL(url) {
// Ask the user if they would like to open link in Safari
let alert = UIAlertController(title: "Open link in Safari?", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (alert: UIAlertAction) -> Void in
// User wants to open in Safari
shared.open(url, options: [:], completionHandler: nil)
}))
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
decisionHandler(.cancel)
}
decisionHandler(.allow)
}
}
This way, when a user clicks a link in the web view, they will be prompted with an alert that will ask them whether or not they would like to open it in Safari, and if it is allowed, Safari will launch and the link will be opened.

iOS firebase: FIRAuthUIDelegate.authUI not being called

I am trying to launch google login from AppDelegate.swift and then launch my app's main screen upon login success.
I am able to
show the google login button as shown above
the user is sent to google to sign in
the user is sent back to original (step 1)
After step 3. I'd like to send the user to my app's main page.
My code is below. The problem I'm having is that authUI is not being called.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, FIRAuthUIDelegate {
var window: UIWindow?
var authUI: FIRAuthUI?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
authUI = FIRAuthUI.defaultAuthUI()
authUI?.delegate = self
let providers: [FIRAuthProviderUI] = [FIRGoogleAuthUI()]
authUI?.providers = providers
// show google login button
let authViewController = authUI?.authViewController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = authViewController
self.window?.makeKeyAndVisible()
return true
}
func application(application: UIApplication, openURL url: NSURL, options: [String: AnyObject]) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url, sourceApplication: options[UIApplicationOpenURLOptionsSourceApplicationKey] as? String, annotation: options[UIApplicationOpenURLOptionsAnnotationKey])
}
func authUI(authUI: FIRAuthUI, didSignInWithUser user: FIRUser?, error: NSError?) {
// launch main view controller
}
}
EDIT: This appears to be a duplicate of another question. The other question's title is quite general and only gets to the details a few lines deep. In any case, I believe Chris's answer is more thorough than the one there. I think both the question and answers here are clearer, more pointed and more thorough so it would be a mistake to just direct people here to go there as would happen if this was marked as a duplicate.
I think your problem lies here, in the - (void)signInWithProviderUI:(id<FIRAuthProviderUI>)providerUI method.
The delegate method is called in the dismissViewControllerAnimated:completion: completion block.
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self.authUI invokeResultCallbackWithUser:user error:error];
}];
As you can see from the Apple docs, this method is expected to be called on a modally presented viewController. You are displaying it as a root view controller. Try displaying it with a modal from a UIViewController, and things should work out. To debug this try and set a breakpoint at line 193 to see that it won't get hit. I would be very surprised if this doesn't work when you display the authController modally.
To come up with a possible solution to your problem (I am assuming you want to ensure a user is signed in before using your app). The below is a simplification of what I am using in an app currently.
EDIT: Updated for the new 1.0.0 FirebaseUI syntax.
class MainTabController: UITabBarController, FIRAuthUIDelegate {
let authUI: FUIAuth? = FUIAuth.defaultAuthUI()
override func viewDidLoad() {
super.viewDidLoad()
var authProviders = [FUIFacebookAuth(), FUIGoogleAuth()]
authUI.delegate = self
authUI.providers = authProviders
//I use this method for signing out when I'm developing
//try! FIRAuth.auth()?.signOut()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isUserSignedIn() {
showLoginView()
}
}
private func isUserSignedIn() -> Bool {
guard FIRAuth.auth()?.currentUser != nil else { return false }
return true
}
private func showLoginView() {
if let authVC = FUIAuth.defaultAuthUI()?.authViewController() {
present(authVC, animated: true, completion: nil)
}
}
func authUI(_ authUI: FUIAuth, didSignInWith user: FIRUser?, error: Error?) {
guard let user = user else {
print(error)
return
}
...
}
It must be a problem of reference.
class AppDelegate: UIResponder, UIApplicationDelegate, FIRAuthUIDelegate {
var window: UIWindow?
let authUI = FIRAuthUI.defaultAuthUI()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
authUI.delegate = self
let providers: [FIRAuthProviderUI] = [FIRGoogleAuthUI()]
authUI.providers = providers
// show google login button
let authViewController = authUI.authViewController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = authViewController
self.window?.makeKeyAndVisible()
return true
}
}
Try this. AppDelegate will hold the reference of authUI and its delegate.