App crashing when it gets closed because of biometrics - ios

I have followed this tutorial to set up authentication on my app.
It works fine, but when try to terminate the app, I get the following:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the
layout engine must not be performed from a background thread after it
has been accessed from the main thread.'
* First throw call stack:
(0x18bf22a48 0x18bc49fa4 0x18c3f8f08 0x18c1fa03c 0x19035664c 0x190357a00 0x18f604c5c 0x18f6004c8 0x18f600734 0x18f600a54
0x18f6054dc 0x18f605328 0x18f5e7004 0x18f97b134 0x18f97b838
0x18f990f70 0x18f989d7c 0x18f98b790 0x18f98dc6c 0x18f98e168
0x18f98dbbc 0x18f98de24 0x100c0f4ec 0x100c01780 0x1017917fc
0x101792bd8 0x1017952d4 0x1017a4160 0x1017a4a88 0x18bc3eb48
0x18bc41760)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
I do not understand the error.
Here's my code:
func authenticateUser() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] success, authError in
DispatchQueue.main.async {
if success {
self?.loginSuccessfull()
} else {
DispatchQueue.main.async {
let ac = UIAlertController(title: "Errore", message: "Riprova", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self?.authenticateUser()
return
}))
self?.present(ac, animated: true, completion: nil)
}
}
}
}
} else {
let ac = UIAlertController(title: "Errore", message: "Il tuo device non è configurato per l'autenticazione", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default))
self.present(ac, animated: true, completion: nil)
}
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] success, authenticationError in
DispatchQueue.global().async {
if success {
DispatchQueue.main.async {
self.loginSuccessfull()
}
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Sorry!", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
As you can see, I just evaluate the biometrics. Again: this works perfectly during the whole "life" of the application, but the problem comes out when I terminate the app. It just crashes and gets me to the home screen.
There are no crash logs into Settings > Privacy...

please use for authentication function its a global function
//enum for response handler
enum AuthenticatinsError: String {
case userEnrolled = "User is not enrolled"
case passCodeNotSet = "user not set passcode"
case biometricNotAvelabel = "Biometric authentication not available"
case faild = "faild to authenticat"
case noIssue = ""
}
func authenticationUser(compleation: #escaping (_ status: Bool, _ msgg: AuthenticatinsError) -> Void) {
context = LAContext()
//check inside app if biometric on then in UserDefault set true other wise false
//let isBiometricOn = WUserDefault.getBiometric()
//if isBiometricOn == false {
//context.invalidate()
//}
// First check if we have the needed hardware support.
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
let reason = "Log in to your account"
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason ) { success, error in
if success {
compleation(true, .noIssue)
} else {
print(error?.localizedDescription ?? "Failed to authenticate")
compleation(false, .faild )
}
}
} else {
if let err = error {
if #available(iOS 11.0, *) {
switch err.code {
case LAError.Code.biometryNotEnrolled.rawValue:
notifyUser("User is not enrolled",
err: err.localizedDescription)
compleation(false, .userEnrolled)
case LAError.Code.passcodeNotSet.rawValue:
compleation(false, .passCodeNotSet)
case LAError.Code.biometryNotAvailable.rawValue:
notifyUser("Biometric authentication not available",
err: err.localizedDescription)
compleation(false, .biometricNotAvelabel)
default:
compleation (false, .passCodeNotSet)
}
} else {
// Fallback on earlier versions
}
}
}
}
user code where you call
self.authenticationUser { (statu, msg) in
if statu == true {
DispatchQueue.main.async {
self?.loginSuccessfull()
}
} else {
DispatchQueue.main.async {
let ac = UIAlertController(title: "Errore", message: "Riprova", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self?.authenticateUser()
return
}))
self?.present(ac, animated: true, completion: nil)
}
}
}

Related

how to enable try Face ID again button in alert when Face ID failed to authenticate in iOS

I am using Face id in my iOS app. Be default when user fails to authenticate try again not working. I want to add callback when user click on try again.Please see my below code.I have searched a lot but nothing found about face id.
func startFaceIDTest(){
attempted = true
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] (successfaceID, authenticationError) in
DispatchQueue.main.async {
if successfaceID {
self.success = true
//self.unlockSecretMessage()
} else {
self.success = false
// let ac = UIAlertController(title: "Authentication failed", message: "You could not be verified; please try again.", preferredStyle: .alert)
// ac.addAction(UIAlertAction(title: "OK", style: .default))
// self.present(ac, animated: true)
}
}
}
} else {
if let err = error {
if #available(iOS 11.0, *) {
self.success = false
switch err.code {
case LAError.Code.biometryNotEnrolled.rawValue:
notifyUser("Your device not enrolled for biometric",
err: err.localizedDescription)
case LAError.Code.passcodeNotSet.rawValue:
notifyUser("A passcode has not been set",
err: err.localizedDescription)
case LAError.Code.biometryNotAvailable.rawValue:
notifyUser("Biometric authentication not available",
err: err.localizedDescription)
default:
notifyUser("Unknown error",
err: err.localizedDescription)
}
} else {
// Fallback on earlier versions
}
}
}
}

Optimizing a CloudKit sign-in on app launch with error handling. How can I better handle the optional chaining? Swift 3.0/Xcode 8.1

Is there a cleaner, swiftier solution to handle the optional chaining happening in my code below? I'm setting up the user for CloudKit access in my custom function runCKsetup():
func runCKsetup() {
container.requestApplicationPermission(.userDiscoverability) { (status, error) in
guard error == nil else {
if let error = error as? NSError {
if let errorDictionary: AnyObject = error.userInfo as? Dictionary<String, AnyObject> as AnyObject? {
let localizedDescription = errorDictionary["NSLocalizedDescription"]! as! String!
if localizedDescription! == "This operation has been rate limited" {
// Create an alert view
let alert = UIAlertController(title: "Network Error", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Test for Connection", style: UIAlertActionStyle.default) { (action) in
self.runCKsetup()
})
self.present(alert, animated: true, completion: nil)
} else {
// Create an alert view
let alert = UIAlertController(title: "Sign in to iCloud", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default) { (action) in
})
self.present(alert, animated: true, completion: nil)
}
}
}
return
}
if status == CKApplicationPermissionStatus.granted {
self.container.fetchUserRecordID { (recordID, error) in
guard error == nil else {
self.presentMessageAlert((error?.localizedDescription)!, title: "Error", buttonTitle: "Ok")
return }
guard let recordID = recordID else { return }
self.container.discoverUserIdentity(withUserRecordID: recordID, completionHandler: { (info, fetchError) in
//do something with the users names: e.g. print("\(info?.nameComponents?.givenName) \(info?.nameComponents?.familyName)")
})
}
}
}
}

Handling Errors in New Firebase and Swift

I'm trying to add error handling in creating user button in iOS project using swift and firebase:
Here's the code for the button:
#IBAction func Register(sender: AnyObject) {
if NameTF.text == "" || EmailTF.text == "" || PasswordTF.text == "" || RePasswordTF == "" || PhoneTF.text == "" || CityTF.text == ""
{
let alert = UIAlertController(title: "عذرًا", message:"يجب عليك ملىء كل الحقول المطلوبة", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
} else {
if PasswordTF.text != RePasswordTF.text {
let alert = UIAlertController(title: "عذرًا", message:"كلمتي المرور غير متطابقتين", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
} else {
FIRAuth.auth()?.createUserWithEmail(EmailTF.text!, password: PasswordTF.text!, completion: { user, error in
print(error)
if error != nil {
let errorCode = FIRAuthErrorNameKey
switch errorCode {
case "FIRAuthErrorCodeEmailAlreadyInUse":
let alert = UIAlertController(title: "عذرًا", message:"الإيميل مستخدم", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeUserNotFound":
let alert = UIAlertController(title: "عذرًا", message:"المستخدم غير موجود", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeInvalidEmail":
let alert = UIAlertController(title: "عذرًا", message:"الإيميل غير صحيح", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
case "FIRAuthErrorCodeNetworkError":
let alert = UIAlertController(title: "عذرًا", message:"خطأ في الاتصال بالانترنت", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
default:
let alert = UIAlertController(title: "عذرًا", message:"خطأ غير معروف", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "نعم", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
}
} else {
FIRAuth.auth()?.signInWithEmail(self.EmailTF.text!, password: self.PasswordTF.text!, completion: { (user: FIRUser?, error: NSError?) in
if let error = error {
print(error.localizedDescription)
} else {
self.ref.child("UserProfile").child(user!.uid).setValue([
"email": self.EmailTF.text!,
"name" : self.NameTF.text!,
"phone": self.PhoneTF.text!,
"city" : self.CityTF.text!,
])
print("Sucess")
// self.performSegueWithIdentifier("SignUp", sender: nil)
}
})
} //else
})
} //Big else
} //Big Big else
}
}//end of
I'm not sure if the syntax of the errors in switch statement is correct or not!
Because when I tested it in the simulator it always gives me the defualt case which is unknown error!
+
I could not find the syntax in the documentation:
https://firebase.google.com/docs/auth/ios/errors
So, What's the correct syntax to add error handling using new firebase and swift!
I've actually just struggled with this for quite a bit of time and found what the issue was. I've tried the code posted in an answer above and the error.code line gave me an error. It did work with error._code though. In other words, credit for the original answer to Paul with a slight modificaiton. Here's my final code (I will edit it for all errors though):
if let errCode = AuthErrorCode(rawValue: error!._code) {
switch errCode {
case .errorCodeInvalidEmail:
print("invalid email")
case .errorCodeEmailAlreadyInUse:
print("in use")
default:
print("Create User Error: \(error)")
}
}
Updated for Swift 4 + Firebase 4 + UIAlertController
extension AuthErrorCode {
var errorMessage: String {
switch self {
case .emailAlreadyInUse:
return "The email is already in use with another account"
case .userNotFound:
return "Account not found for the specified user. Please check and try again"
case .userDisabled:
return "Your account has been disabled. Please contact support."
case .invalidEmail, .invalidSender, .invalidRecipientEmail:
return "Please enter a valid email"
case .networkError:
return "Network error. Please try again."
case .weakPassword:
return "Your password is too weak. The password must be 6 characters long or more."
case .wrongPassword:
return "Your password is incorrect. Please try again or use 'Forgot password' to reset your password"
default:
return "Unknown error occurred"
}
}
}
extension UIViewController{
func handleError(_ error: Error) {
if let errorCode = AuthErrorCode(rawValue: error._code) {
print(errorCode.errorMessage)
let alert = UIAlertController(title: "Error", message: errorCode.errorMessage, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
}
}
Usage example:
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!._code)
self.handleError(error!) // use the handleError method
return
}
//successfully logged in the user
})
Even though this has been answered correctly, wanted to share a nice implementation for this we added to our project.
This can be done for other error types as well, but we just needed it for the FIRAuthErrorCodes.
If you extend FIRAuthErrorCode to have a variable errorMessage of type string, you can have your own error messages for the users:
extension FIRAuthErrorCode {
var errorMessage: String {
switch self {
case .errorCodeEmailAlreadyInUse:
return "The email is already in use with another account"
case .errorCodeUserDisabled:
return "Your account has been disabled. Please contact support."
case .errorCodeInvalidEmail, .errorCodeInvalidSender, .errorCodeInvalidRecipientEmail:
return "Please enter a valid email"
case .errorCodeNetworkError:
return "Network error. Please try again."
case .errorCodeWeakPassword:
return "Your password is too weak"
default:
return "Unknown error occurred"
}
}
}
You could customize only some as we have above and group the rest under "Unknown error".
With this extension you can handle an error as shown in Vladimir Romanov's answer:
func handleError(_ error: Error) {
if let errorCode = FIRAuthErrorCode(rawValue: error._code) {
// now you can use the .errorMessage var to get your custom error message
print(errorCode.errorMessage)
}
}
FIRAuthErrorCode is an int enum not a string enum. Do the following:
if let error = error {
switch FIRAuthErrorCode(rawValue: error.code) !{
case .ErrorCodeInvalidEmail:
More info in this answer.
I am using Swift 5 and Firebase 6.4.0 and for me, none of the above really worked. After trying around a bit I came up with this:
Auth.auth().createUser(withEmail: emailTextfield.text!, password: passwordTextfield.text!) { (user, error) in
if error!= nil{
let alert = UIAlertController(title: "Error", message: error!.localizedDescription, preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert,animated: true)
}

why is the block of code inside func call is not executed?

I am trying to implement an Xmpp client into my application.I am planning on using the following code inside my application but some part of the code doesn't seem to be executed which prevents me to connect to a server. https://github.com/processone/xmpp-messenger-ios
I am trying to connect to a server with my jabberID and password but for some this code is never executed:
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do smt
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
self.dismissViewControllerAnimated(true, completion: nil)
}
Here is the whole code that is related to the problem:
#IBAction func validate(sender: AnyObject) {
if OneChat.sharedInstance.isConnected() {
OneChat.sharedInstance.disconnect()
usernameTextField.hidden = false
passwordTextField.hidden = false
validateButton.setTitle("Validate", forState: UIControlState.Normal)
} else {
OneChat.sharedInstance.connect(username: self.usernameTextField.text!, password: self.passwordTextField.text!) {(stream, error) -> Void in
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do smt
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
}
}
public typealias OneChatConnectCompletionHandler = (stream: XMPPStream, error: DDXMLElement?) -> Void
public func connect(username username: String, password: String, completionHandler completion:OneChatConnectCompletionHandler) {
if isConnected() {
streamDidConnectCompletionBlock = completion
self.streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: nil)
return
}
if (username == "kXMPPmyJID" && NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) == "kXMPPmyJID") || (username == "kXMPPmyJID" && NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) == nil) {
streamDidConnectCompletionBlock = completion
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Please set crendentials before trying to connect"))
return
}
if username != "kXMPPmyJID" {
setValue(username, forKey: kXMPP.myJID)
setValue(password, forKey: kXMPP.myPassword)
}
if let jid = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) {
xmppStream?.myJID = XMPPJID.jidWithString(jid)
} else {
streamDidConnectCompletionBlock = completion //was false
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Bad username"))
}
if let password = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myPassword) {
self.password = password
} else {
streamDidConnectCompletionBlock = completion //was false
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Bad password"))
}
try! xmppStream!.connectWithTimeout(XMPPStreamTimeoutNone)
streamDidConnectCompletionBlock = completion
}
Perhaps, you are expecting a time-out that never will happened (configuration of the library)
I had the same problem and, finally, I fount that my problem was hostname and port. Did you try to use something like this:
let usuario = "name#domain.com";
OneChat.sharedInstance.connect(username: usuario, password: pass) { (stream, error) -> Void in
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do something
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
print("I did it!")
}
}

Unexpectedly found nil while unwrapping optional.

I've narrowed down the error to this block of code. I need to define something at optional but I'm not sure where.
#IBAction func SignupWithFacebook(sender: AnyObject) {
var permissionsArray = ["user_about_me", "user_birthday", "email"]
PFFacebookUtils.logInWithPermissions(permissionsArray, block: { (user: PFUser?, error: NSError!) -> Void in
if (user == nil) {
let errormessage = error.userInfo!["error"] as NSString
var facebookLoginError = UIAlertController(title: "Error While Logging", message: "\(errormessage)", preferredStyle: .Alert)
var okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
facebookLoginError.addAction(okButton)
self.presentViewController(facebookLoginError, animated: true, completion: nil)
}
})
}
Any help would be greatly appreciated.
Your code assumes that if user is nil then error will exist. I don't know if you can make that guarantee. Try this instead:
#IBAction func SignupWithFacebook(sender: AnyObject) {
let permissionsArray = ["user_about_me", "user_birthday", "email"]
PFFacebookUtils.logInWithPermissions(permissionsArray) { (user, error) -> Void in
if user == nil {
if let e = error {
let errormessage = e.userInfo!["error"] as NSString
let facebookLoginError = UIAlertController(title: "Error While Logging", message: "\(errormessage)", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
facebookLoginError.addAction(okButton)
self.presentViewController(facebookLoginError, animated: true, completion: nil)
}
}
}
}

Resources