Firebase AuthUI not triggering callback function with Swift - swift

I recently updated an app I'm working on to IOS15 and I've hit a snag while testing.
When the user tries to log into the app using FirebaseUI (with google or email) the login process completes but does not trigger the callback function didSignInWith: from the delegate.
None of the print statements are being called from this class, but if I restart the app it behaves as if login was successful
Does anybody know what I've done wrong here?... as far as I can tell everything should work fine
import UIKit
import UserNotifications
import FirebaseUI
class LoginViewCell: UICollectionViewCell, FUIAuthDelegate {
let imageView: UIImageView
let title: UILabel
let text: UILabel
var parent: WelcomeViewController? = nil
var authUI: FUIAuth
override init(frame: CGRect) {
authUI = FUIAuth.defaultAuthUI()!
super.init(frame: frame)
let providers: [FUIAuthProvider] = [FUIGoogleAuth(authUI: authUI), FUIEmailAuth()
]
authUI.providers = providers
authUI.delegate = self
let loginButton = UIButton(frame: CGRect(x: 100, y: 100, width: 300, height: 50))
loginButton.backgroundColor = .blue
loginButton.setTitle("Let's Get Started!", for: .normal)
loginButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
loginButton.center = CGPoint(x: frame.width / 2, y: ((frame.height/4) * 3) + 50)
addSubview(loginButton)
}
#objc func buttonAction(sender: UIButton!) {
let authViewController = authUI.authViewController()
parent?.present(authViewController , animated: true, completion: nil)
}
//MARK: Authentication Delegate
func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {
// handle user and error as necessary
print("Login attempted")
if( error != nil){
print("Error Logging In \(String(describing: error))")
}else{
userHandler.doesUserExist({(exists: Bool) in
if(exists){
self.parent?.dismiss(animated: true, completion: nil)
}else{
let manager = UserManagerViewController()
manager.shouldCreateUser = true
let nvc = CalendarNavigationController(rootViewController: manager)
self.parent?.present(nvc, animated: true, completion: nil)
}
})
}
}
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as! String?
if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication) ?? false {
print("Think did log in successful")
return true
}
// other URL handling goes here.
print("think did not log in")
return false
}
}

Is your cell still displayed when the callback should get called ? If not then here's the problem :
Your cell object can be deallocated (managed by the UICollectionView), and it happens that it's also the delegate for firebase. When that happens, firebase call a delegate that is nil, and it does nothing.
What you can do is create a specific class that will conform to the delegate protocol, keep a strong reference to it in a controller or viewModel that won't be terminated unless you want it (here the controller of viewModel is the object that initialize the cell), pass it as a dependency to the cell object (dependency injection), and set it as the authUI.delegate. If you follow these instructions the delegate will be retained cause it has a strong reference.

Related

blank collection view in user login for the first time

Good day everyone,
I have root view controller set up as my HomeViewController, in this project i am using token based authentication ( which i am storing in user defaults ) and i am using token for all my API calls.
I have a check in my viewWillAppear method to check if there is access token present and then i make the api call in viewDidAppear to populate the collection view, and this works perfectly fine at all times except the first time.
If I log in for the first time it hits the viewWillAppear, viewDidAppear and then login screen pops up and once i authenticate the user and save it in the UserDefaults, dismiss the login screen and in the HomeViewController all i get is a spinner ( which means that viewDidAppear is also been called ) but if i close the app and open it again it all works fine.
What can i change in my code to make it work in the first time please and thank you!!
class HomeViewController: UIViewController {
// MARK: - Properties
let refreshControl = UIRefreshControl()
var publishedReportList: [ReportListDetail] = []
private let reportsCollectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.register(ReportsCollectionViewCell.self, forCellWithReuseIdentifier: ReportsCollectionViewCell.identifier)
collectionView.backgroundColor = .systemBackground
return collectionView
}()
// MARK: - Initialisation
override func viewDidLoad() {
super.viewDidLoad()
reportsCollectionView.delegate = self
reportsCollectionView.dataSource = self
print(publishedReportList.count)
refreshControl.tintColor = .blue
refreshControl.addTarget(self, action: #selector(pullToRefresh), for: .valueChanged)
reportsCollectionView.addSubview(refreshControl)
reportsCollectionView.alwaysBounceVertical = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// check auth status
handleNotAuthenticated()
}
override func viewDidAppear(_ animated: Bool) {
// call for reports
getReportUserLayout()
}
// MARK: - Handlers
#objc func pullToRefresh() {
// Code to refresh table view
getReportUserLayout()
}
fileprivate func getReportUserLayout() {
// publishedReportList.removeAll()
whySuchEmptyLabel.isHidden = true
spinner.show(in: view)
spinner.textLabel.text = "Loading Reports.."
DispatchQueue.main.async {
ReportsManager.shared.getReportData { [weak self] (listOfReports) in
guard let strongSelf = self else { return }
strongSelf.publishedReportList = listOfReports
if listOfReports.count == 0 {
strongSelf.whySuchEmptyLabel.isHidden = false
}
strongSelf.reportsCollectionView.reloadData()
strongSelf.spinner.dismiss()
strongSelf.refreshControl.endRefreshing()
}
}
}
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
// show login view controller
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
present(loginVC, animated: false)
}
}
}
You are in the HomeViewController and presenting loginVC, so it will not trigger viewDidAppear or viewWillAppear because it is not disappeared from the app. You have to use closure or delegate or notification to communicate back to the HomeViewController. You can also use the Combine framework and save the state. Here is an example of using delegate.
// Add protocol
protocol ViewControllerDelegate {
func loggedIn()
}
class HomeViewController: UIViewController, ViewControllerDelegate {
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
loginVC.viewDelegate = self
present(loginVC, animated: false)
}
}
}
class LoginViewController: UIViewController {
var viewDelegate: ViewControllerDelegate? = nil
func userLoggedIn() {
self.viewDelegate?.loggedIn()
}
}

Sign In with Google Authentication

I've implemented Google Authentication via Firebase in my App. Everything works smoothly except for one small problem that I can't seem to find. Whenever the user opens the page that prompts them to "Sign in with Google" (ie. login page or sign up page), the banner appears momentarily before disappearing. I do not want it to appear at all, unless the user clicks the "Sign in with Google" button. How can I get rid of this?
WelcomeViewController (the view controller with the google login)
import UIKit
import FirebaseAuth
import Firebase
import FBSDKLoginKit
import GoogleSignIn
class WelcomeViewController: UIViewController, GIDSignInDelegate {
#IBOutlet weak var stackView: UIStackView!
#IBOutlet weak var signInFacebookButton: UIButton!
#IBOutlet weak var signInGoogleButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setUpGoogleButton()
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
}
// SIGN IN WITH GOOGLE
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google")
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
// Successfully logged in
guard let uid = user?.user.uid else { return }
print("Successfully logged into Firebase with Google", uid)
// switch to tab bar controller
let tabBarC = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") as! TabBarController
tabBarC.modalPresentationStyle = .fullScreen
self.present(tabBarC, animated: true, completion: nil)
print("Switched to TabBarController")
})
}
fileprivate func setUpGoogleButton() {
let button = signInGoogleButton
button?.layer.borderWidth = 0
button?.backgroundColor = UIColor.init(red: 130/255, green: 178/255, blue: 189/255, alpha: 1)
button?.layer.cornerRadius = 20.0
button?.tintColor = UIColor.white
button!.addTarget(self, action:
#selector(handleCustomGoogleSignIn), for: .touchUpInside)
GIDSignIn.sharedInstance()?.delegate = self
}
#objc func handleCustomGoogleSignIn() {
GIDSignIn.sharedInstance().signIn()
}
I've attached a link to a screen recording of what happens. The second page shown in the screen recording is identical to the code below, so it has the same problem. Any help is appreciated, thank you!
https://drive.google.com/file/d/1t4KV0Z6qwfCK56Gf2314wXWhAeQR0wUs/view?usp=sharing
That's because of your code inside viewDidLoad(). You are implementing this method:
GIDSignIn.sharedInstance().signIn()
This triggers the sign in method as soon as the view loads (as you are implementing it inside viewDidLoad()), and that causes that momentary sign in pop up that disappears.
Instead of implementing that method there, you should only implement it inside your handleCustomGoogleSignIn().
Conclusion, your viewDidLoad() should look like this:
override func viewDidLoad() {
super.viewDidLoad()
setUpGoogleButton()
GIDSignIn.sharedInstance()?.presentingViewController = self
}

Integrating Unity+Vuforia to existing iOS project make user interaction not working

Currently I'm following this tutorial to integrate Unity+Vuforia project to my existing iOS project. I manage to be able to show the Unity view inside my ARViewController. The thing is I lost all the user interaction in my view controller: my touch event for back button not fire up.
import Foundation
class ARViewController: UIViewController {
var unityView: UIView?
static func instantiateViewController() -> ARViewController {
let controller = UIStoryboard.main.instantiateViewController(withIdentifier: "ARViewController") as! ARViewController
return controller
}
override func viewDidLoad() {
super.viewDidLoad()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.currentUnityController = UnityAppController()
appDelegate.currentUnityController?.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
appDelegate.startUnity()
NotificationCenter.default.addObserver(self, selector: #selector(handleUnityReady), name: NSNotification.Name("UnityReady"), object: nil)
}
}
#IBAction func onBackPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.stopUnity()
}
}
#objc func backPressedTest() {
}
#objc func handleUnityReady() {
showUnitySubView()
}
func showUnitySubView() {
guard let unityView = UnityGetGLView() else { return }
self.unityView = unityView
// insert subview at index 0 ensures unity view is behind current UI view
view?.addSubview(unityView)
unityView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": unityView]
let w = NSLayoutConstraint.constraints(withVisualFormat: "|-0-[view]-0-|", options: [], metrics: nil, views: views)
let h = NSLayoutConstraint.constraints(withVisualFormat: "V:|-50-[view]-0-|", options: [], metrics: nil, views: views)
view.addConstraints(w + h)
let button = UIButton(type: .custom)
button.setImage(#imageLiteral(resourceName: "ic_back_black").withRenderingMode(.alwaysTemplate), for: .normal)
button.addTarget(self, action:#selector(backPressed), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 28, height: 60)
button.widthAnchor.constraint(equalToConstant: button.frame.width).isActive = true
button.tintColor = UIColor.purpleBrown()
view?.addSubview(button)
}
}
I also notice that button from Unity also have any effect when I touch it. The back button inside the green bar is from Unity. And the blue button is from my ARViewController. Both seem don't reach to touch event.
Debug elements:
It happens when I put the Unity configuration at the top of application:didFinishLaunchingWithOptions:, above my existing configuration for another services that I use in the project. For someone who encounter this problem in the future, here's my appDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.application = application
// Another existing settings for the project, e.g. fabric
Fabric.with([Crashlytics.self])
// Put the Unity configuration at the bottom of the function
unity_init(CommandLine.argc, CommandLine.unsafeArgv)
currentUnityController = UnityAppController()
currentUnityController?.application(application, didFinishLaunchingWithOptions: launchOptions)
startUnity()
stopUnity()
return true
}
For the viewWillAppear(_:) in the view controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupBackBarButtonItems(back: true, isDark: true)
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.startUnity()
showUnitySubView()
}
}
As mentioned by #Prashant in the comment, the UnityReady notification only get called once. So I don't use it.
Then I just call stopUnity() in viewWillDisappear(_:):
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.stopUnity()
}
}
The current problem is I can't kill the unity process if I leave the screen. It's known bug and I'm still figuring out how to do that, if it's possible.
I had the same issue but moving Unity configuration to the end of applicationDidFinishLaunchingWithOption method didn't solve it and I still had a UIWindow in front of my screen that steals all user interactions.
My solution is not to create a new window in UnityAppController.mm, but using the current application keyWindow.
Replace:
_window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
with:
_window = [UIApplication sharedApplication].keyWindow;

Interstitial iAd delayed after pressing button

I'm trying to setup iAd after clicking cancel button on JSSAlert. I have in JSSAlert function that set alpha for full view 0.7.. And in view controller I have function for iAd and set back alpha to 1.0...
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Vyhodnotenie testu"
self.showAlert()
}
func showAlert() {
func callback(){}
if numberOfPoints > 49 {
let customIcon = UIImage(named: "smile")
let alertview = JSSAlertView().show(self, title: "Gratulujeme! Uspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0x22c411, alpha: 1), iconImage: customIcon)
alertview.setTextTheme(.Light)
alertview.addAction(myCancelCallback)
self.navigationController?.navigationBar.alpha = 0.7
} else {
let customIcon = UIImage(named: "sad")
let alertview = JSSAlertView().show(self, title: "Ľutujeme! Neuspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0xd20606, alpha: 1), iconImage: customIcon)
alertview.addAction(myCancelCallback)
alertview.setTextTheme(.Light)
self.navigationController?.navigationBar.alpha = 0.7
}
}
func myCancelCallback() {
self.navigationController?.navigationBar.alpha = 1.0
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Automatic
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
Alpha back to 1.0 in function myCancelCallback is working but iAd is delayed.. What can cause that delay? Or how can I deal with it?
I want to show iAd immediately after pressing OK!.
Video how it's working:
https://www.youtube.com/watch?v=r6LKN-cjaz8&feature=youtu.be
Update:
iAd App Network Shutdown As of December 31, 2016, the iAd App Network
is no longer available. If you'd like to promote your apps, you can
advertise using Search Ads, Apple News, or third party networks and
advertising sellers.
Reference: https://developer.apple.com/support/iad/
Here is what your gonna do, you have to create the interstitial including close button programmatically , i just made you a sample :
Add row in info.plist : View controller-based status bar appearance -> NO
in AppDelegate didFinishLaunchingWithOptions method add the following line to insure that status bar will be not hidden :
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
Here is a full sample view controller of how your gonna add the interstitial programmatically , but you only need to write animation when the interstitial is showing instead of just using addSubView. you can use animate transform translation you will find a lot of samples about that.
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "loadInterstitialAd", userInfo: nil, repeats: false)
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
print("interstitialAdWillLoad")
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
print("interstitialAdDidLoad")
UIApplication.sharedApplication().statusBarHidden = true
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
self.navigationController?.navigationBar.addSubview(interstitialAdView)
closeButton = UIButton(frame: CGRect(x: 15, y: 15, width: 20, height: 20))
//add a cross shaped graphics into your project to use as close button
closeButton.setBackgroundImage(UIImage(named: "close"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.navigationController?.navigationBar.addSubview(closeButton)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func close() {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitialAd = nil
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
print("interstitialAdActionDidFinish")
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("didFailWithError")
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("interstitialAdDidUnload")
close()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
Update: It might be you just have to call : UIViewController.prepareInterstitialAds() in viewDidLoad of your class to download the content of the interstitial so whenever you ask to present it will be ready and then might be no delay.

Passing OAuth token between views programmatically in Swift

I am stuck on trying to pass a variable from one view to another. I am deliberately doing this all programmatically without Story Boards.
ViewController creates has a UIButton which when pressed calls the AuthoriseController.
AuthoriseController then loads a UIWebView, opening it at a particular URL e.g. https://foursquare.com/oauth2/authenticate?client_id=xxxxx&response_type=token&redirect_uri=yyyyy
This loads up an OAuth screen. After the user logs in, control comes back to AuthoriseController where I extract the access_token which is returned in the redirect URI.
The exctraction works great and I can get hold of the token, my problem is that I want to get the the token back to my ViewController so that when the user pushes the button again it will be available to an HTTP request (at the moment I’m just printing the token).
My ViewController and AuthoriseController implement a Protocol called DataProtocol which I was hoping could be used share the token between controllers.
ViewController:
import UIKit
class ViewController: UIViewController {
var delegate: DataProtocol? = nil
func setUpView(){
let firstView = UIView()
let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
firstView.setTranslatesAutoresizingMaskIntoConstraints(false)
firstView.backgroundColor = UIColor(red: 0.75, green: 0.75, blue: 0.1, alpha: 1.0)
view.addSubview(firstView)
setUpButton(button)
firstView.addSubview(button)
setUpConstraints(firstView, button)
}
func setUpConstraints(firstView: UIView, _ button: UIButton) {
let viewsDictionary = ["firstView":firstView,"button":button]
let metricsDictionary = ["firstViewHeight":1334.0,"viewWidth":750.0 ]
let firstViewHeightConstraintH:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:[firstView(viewWidth)]", options: NSLayoutFormatOptions(0), metrics: metricsDictionary, views: viewsDictionary)
let firstViewHeightConstraintV:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:[firstView(firstViewHeight)]", options: NSLayoutFormatOptions(0), metrics: metricsDictionary, views: viewsDictionary)
firstView.addConstraints(firstViewHeightConstraintH)
firstView.addConstraints(firstViewHeightConstraintV)
let buttonConstraintH:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-150-[button(>=80)]", options: NSLayoutFormatOptions.AlignAllCenterY, metrics: nil, views: viewsDictionary)
let buttonConstraintV:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-300-[button]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
firstView.addConstraints(buttonConstraintH)
firstView.addConstraints(buttonConstraintV)
}
func setUpButton(button: UIButton) {
button.setTranslatesAutoresizingMaskIntoConstraints(false)
button.setTitle("Authorise", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonPressed", forControlEvents: UIControlEvents.TouchUpInside)
button.backgroundColor = UIColor.blueColor()
button.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
override func shouldAutorotate() -> Bool {
return false
}
func buttonPressed(){
let ac = AuthoriseController()
self.presentViewController(ac, animated: true, completion: nil)
println("token in ViewController.buttonPressed: [\(self.delegate?.token)]")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 1, alpha: 1.0)
setUpView()
}
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.All.toRaw())
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
AuthoriseController:
import UIKit
import WebKit
class AuthoriseController: UIViewController, UIWebViewDelegate, DataProtocol {
var token: String = ""
let AUTH_URL = "https://foursquare.com/oauth2/authenticate?client_id=";
let RESP_TYPE_AND_KEY_AND_TOKEN = "&response_type=token&redirect_uri=";
let REDIRECT_URI : String = "ZZZZZZZZ";
let CLIENT_ID : String = "XXXXXXX";
var webview: UIWebView = UIWebView()
override func viewDidLoad() {
super.viewDidLoad()
var myViewController = ViewController()
self.webview.frame = self.view.bounds
self.webview.delegate = self
self.view.addSubview(self.webview)
var url: NSURL = NSURL.URLWithString(AUTH_URL + CLIENT_ID + RESP_TYPE_AND_KEY_AND_TOKEN + REDIRECT_URI)
var urlRequest: NSURLRequest = NSURLRequest(URL: url)
self.webview.loadRequest(urlRequest)
}
func webView(webView: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
token = getToken(request.URL.absoluteString!)
println("token in AuthoriseController.webView: [\(token)]")
if(!token.isEmpty) {
self.dismissViewControllerAnimated(true, completion: nil)
}
return true
}
func getToken(url: String) -> String {
var token = ""
if url.rangeOfString("#access_token") != nil{
token = url.componentsSeparatedByString("=").last!
}
return token
}
}
Here is the DataProtocol:
import Foundation
protocol DataProtocol {
var token : String { get set }
}
When I doing this:
Starting the App
Pressing the Authorise button
Successfully logging in (control returns to ViewController
Pressing the button again
Output:I get this output:
token in ViewController.buttonPressed: [nil]
token in AuthoriseController.webView: []
token in AuthoriseController.webView: []
token in AuthoriseController.webView: []
token in AuthoriseController.webView: [VSKRNHJRS3NWPN3EEIYMCSZYJ2YMNSH4GBXNQFL1EMYLJ5TO]
token in ViewController.buttonPressed: [nil]
token in AuthoriseController.webView: []
token in AuthoriseController.webView: [VSKRNHJRS3NWPN3EEIYMCSZYJ2YMNSH4GBXNQFL1EMYLJ5TO]
I’d hoped to see the token in the second occurrence of ViewController.buttonPressed, instead of NIL.
Can anyone see where I am going wrong? Any help is greatly appreciated! Steve
It looks like the delegate is hooked up correctly but you aren't sending the token to it. In AuthoriseController, you need to set the token for the delegate:
func webView(webView: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
token = getToken(request.URL.absoluteString!) // sets the token in the AuthoriseController
println("token in AuthoriseController.webView: [\(token)]")
if(!token.isEmpty) {
// pass the token to the delegate
self.delegate?.token = token
self.dismissViewControllerAnimated(true, completion: nil)
}
return true
}