threading error for performSegue with viewController? iOS - ios

I am trying to jump to a second view controller after I have authorized a user's TouchID. I am able to validate that the TouchID is working but I am having an issue of jumping to a second viewController.
I have created a SecondViewController and a Segue with the Identifier "dispenseScreen". However, whenever I try to jump to the second screen my program crashes.
#IBAction func touchID(_ sender: Any)
{
let context:LAContext = LAContext()
//Removes Enter Password during failed TouchID
context.localizedFallbackTitle = ""
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
{
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "We require your TouchID", reply: { (wasCorrect, error) in
self.isBiometryReady()
if wasCorrect {
self.performSegue(withIdentifier: "dispenseScreen", sender: self)
print("Correct")
}
else {
print("Incorrect")
}
})
} else {
//Enter phone password if too many login attempts
//Add message alerting user that TouchID is not enabled
}
}
There are no semantic errors in my code but I am receiving a threading error when I try to go to the second view controller.

You're trying to do the segue in the callback from evaluatePolicy. For anything involving UI, you need to make sure you're on the main thread: (wasCorrect, error) in DispatchQueue.main.async { ... }

Related

How to navigate to another page when faceId identified?

when click button I added face id recognition and if true, need to navigate another page. But not working. application crashed. After few minutes navigate to the next page and back button not working.
#IBAction func myProfile(_ sender: Any) {
// self.performSegue(withIdentifier: "MyProfile", sender: nil)
let context:LAContext = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil){
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Need to access with your finger print", reply: {(wasCorrect, error) in
if wasCorrect{
print("correct")
self.performSegue(withIdentifier: "MyProfile", sender: nil)
}else{
print("incorrect")
}
})
}else{
print("finger print doesn't support touch id")
}
}
Error message is:
[Animation] +[UIView setAnimationsEnabled:] being called from a
background thread. Performing any operation from a background thread
on UIView or a subclass is not supported and may result in unexpected
and insidious behavior.
Callback of evaluatePolicy runs in a background queue so do
DisptachQueue.main.async {
self.performSegue(withIdentifier: "MyProfile", sender: nil)
}

Can't insert message after TouchID authentication

So I'm playing around a bit with iMessage apps, and have hit a weird issue. I want to try and use TouchID authentication inside of iMessage, and am able to pop the TouchID alert fine from the iMessage app. However, when I go to insert a message showing the result of TouchID, it won't insert the message for me. Here's the relevant code:
#IBAction func authenticateTapped(_ sender: Any) {
let context = LAContext()
var wasSuccessful = false
self.group.enter()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Testing authentication") { (successful, _) in
wasSuccessful = successful
self.group.leave()
}
self.group.notify(queue: DispatchQueue.main) {
self.sendResult(wasSuccessful)
}
}
#IBAction func sendMessageTapped(_ sender: Any) {
sendResult(true)
}
func sendResult(_ successful: Bool) {
guard let conversation = self.activeConversation else { fatalError("expected conversation") }
var components = URLComponents()
components.queryItems = [URLQueryItem(name: "successful", value: successful.description)]
let layout = MSMessageTemplateLayout()
layout.image = UIImage(named: "green_checkmark")
layout.caption = "Authentication Result"
let message = MSMessage(session: conversation.selectedMessage?.session ?? MSSession())
message.url = components.url!
message.layout = layout
print("queryParts: \(String(describing: components.queryItems))")
print("message: \(message)")
print("activeConversation: \(String(describing: conversation))")
conversation.insert(message) {
(error) in
print("in completion handler")
print(error ?? "no error")
}
}
When authenticateTapped is triggered, the TouchID prompt shows, I successfully authenticate, and then see every log message inside of the sendResult message, except for any of the ones in the completion handler of the insert method.
The weird thing is, when the sendMessageTapped method is fired, everything works as expected. Does anyone know what's going on here, and why I can't seem to insert a message after I successfully authenticate using TouchID?
The only thing I can think of that's different between the two is that the view controller is disappearing when the TouchID prompt comes up, however, if that were the cause, I would expect none of my print statements would show up in the console, when everyone does except those in the completion handler?
Edit: I've done a bit more digging. When presenting the Touch ID authentication in compact mode, your view controller resigns active. When presenting in expanded mode, it stays active, allowing you to insert the message.
Does anyone know if resigning active when presenting the Touch ID alert is a bug or intended behavior?

IOS app in swift 3 not navigating to further viewcontrollers

I am doing project on secured authentication....when i try to register my email id and password it is not going to next viewcontroller...when i run my code it is working well and not showing any errors but i am not getting output...here is my code
#IBAction func signInButtonTapped(_ sender: UIButton) {
// TODO: Do some form validation on the email and password
if let email = emailTextField.text, let pass = passwordTextField.text {
// Check if it's sign in or register
if isSignIn {
// Sign in the user with Firebase
FIRAuth.auth()?.signIn(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.performSegue(withIdentifier: "goToHome", sender: self)
}
else {
// Error: check error and show message
self.displayAlertMessage(messageToDisplay: "Password didn't match");
}
})
}
else {
// Register the user with Firebase
FIRAuth.auth()?.createUser(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.performSegue(withIdentifier: "goToEnroll", sender: self)
}
else {
// Error: check error and show message
}
})
}
}
Please check the connection in Storyboard, make sure that the segue name of link between Current VC -> Home VC is: "goToHome"
Check the same for Enroll VC
Check here if the idenfier is same as you have given in your story board.
Note: Please check first that the identifier you have mentioned here is same as your storyboard one.

Attempt to present TabBarController on FBSDKContainerViewController whose view in not in the window hierarchy

I'm using Facebook login in my app and I'm not able to go to next view after login completed.
i.e. TabBarController its shows an above stated error. I am doing custom login from a button. Here is my code of how I am doing login.
#IBAction func fbLoginBtn(sender: AnyObject) {
facebookLogin.logInWithReadPermissions(["email","user_friends","user_posts"],fromViewController: self, handler: { (response:FBSDKLoginManagerLoginResult!, error: NSError!) in
if error != nil {
print(error.localizedDescription)
print("Facebook login failed")
} else {
let accessToken = response.token.tokenString
print(accessToken)
print("Successfully login.\(accessToken)")
self.performSegueWithIdentifier("showMain", sender: self)
}
})
}
After login I'm performing segue to go to next view i.e. TabBarController. If anyone can help please help me. Thank you.

Firebase - iOS Swift: FIRAuth.auth().signOut() not signing out current user

I'm building an app using Firebase with an initial SignInViewController that loads a sign in page for users to authenticate with email which triggers the following methods:
#IBAction func didTapSignIn(sender: AnyObject) {
let email = emailField.text
let password = passwordField.text
FIRAuth.auth()?.signInWithEmail(email!, password: password!) { (user, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(user!)
}
}
func signedIn(user: FIRUser?) {
AppState.sharedInstance.displayName = user?.displayName ?? user?.email
AppState.sharedInstance.signedIn = true
NSNotificationCenter.defaultCenter().postNotificationName(Constants.NotificationKeys.SignedIn, object: nil, userInfo: nil)
performSegueWithIdentifier(Constants.Segues.SignInToHome, sender: nil)
}
The SignInViewController also checks if there is a cached current user when the app launches and, if so, signs that user in:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
//Synchronously gets the cached current user, or null if there is none.
if let user = FirebaseConfigManager.sharedInstance.currentUser {
self.signedIn(user)
}
}
Once the user is signed in, the app segues to a HomeScreenViewController which displays a "Sign Out" button at the top left of the navigation bar. When a user taps the "Sign Out" button, that user is supposed to get signed out and the app should segue back to the SignInViewController with the following method:
#IBAction func didTapSignOut(sender: UIBarButtonItem) {
print("sign out button tapped")
let firebaseAuth = FIRAuth.auth()
do {
try firebaseAuth?.signOut()
AppState.sharedInstance.signedIn = false
dismissViewControllerAnimated(true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError)")
} catch {
print("Unknown error.")
}
}
When I tap the "Sign out" button, the didTapSignOut method gets called and gets executed.
However, after the try firebaseAuth?.signOut() line of code gets executed, the current user should be nil. But when I print out the current user in the Xcode console, the current user is still logged in:
po FIRAuth.auth()?.currentUser
▿ Optional<FIRUser>
- Some : <FIRUser: 0x7fde43540f50>
Since the current user doesn't get signed out after firebaseAuth?.signOut() gets called, once the app segues back to the SignInViewController the app still thinks there is a cached current user so that user gets signed in again.
Could this be a Keychain issue?
Does it have to do with NSNotificationCenter.defaultCenter().postNotificationName being called?
My code comes directly from the Google Firebase Swift Codelab so I'm not sure why it's not working:
https://codelabs.developers.google.com/codelabs/firebase-ios-swift/#4
You can add a listener in your viewDidAppear method of your view controller like so:
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
print("User is signed in.")
} else {
print("User is signed out.")
}
}
This allows you to execute code when the user's authentication state has changed. It allows you to listen for the event since the signOut method from Firebase does not have a completion handler.
GIDSignIn.sharedInstance().signOut()
Use exclamation points not question marks.
try! FIRAuth.auth()!.signOut()
I actually had this issue as well. I was also logging out the user (as you are) with the method's provided by Firebase but when I printed to the console it said that I still had a optional user.
I had to change the logic of setting the current user so that it is always configured by the authentication handler provided by Firebase:
var currentUser: User? = Auth.auth().currentUser
var handle: AuthStateDidChangeListenerHandle!
init() {
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
self.currentUser = user
if user == nil {
UserDefaults.standard.setValue(false, forKey: UserDefaults.loggedIn)
} else {
UserDefaults.standard.setValue(true, forKey: UserDefaults.loggedIn)
}
}
}
As long as you are referencing the current user from this handle, it will update the current user no matter the authentication state.
Some answers are using a force unwrap when the firebase signing out method can throw an error. DO NOT DO THIS!
Instead the call should be done in a do - catch - block as shown below
do {
try Auth.auth().signOut()
} catch let error {
// handle error here
print("Error trying to sign out of Firebase: \(error.localizedDescription)")
}
You can then listen to the state change using Auth.auth().addStateDidChangeListener and handle accordingly.
I just had what I think is the same problem - Firebase + Swift 3 wouldn't trigger stateDidChangeListeners on logout, which left my app thinking the user was still logged in.
What ended up working for me was to save and reuse a single reference to the FIRAuth.auth() instance rather than calling FIRAuth.auth() each time.
Calling FIRAuth.auth()?.signOut() would not trigger stateDidChangeListeners that I had previously attached. But when I saved the variable and reused it in both methods, it worked as expected.

Resources