UISwitch().setOn does not work (seemly) - ios

here is the uiswitch valueChange action
#IBAction func swichTouchIdState(sender: UISwitch) {
print("newState:\(sender.on)")
if sender.on == false {
AuthenticationSetter.touchIdAuthentication() {
isSuccess, error in
if isSuccess == true {
AuthenticationSetter.setAuthenticationSwitchUserDefault(false)
} else {
sender.setOn(true, animated: true)
}
if let laError = error as? LAError {
print("error happened in authenticating: \(laError)")
switch laError {
case .AppCancel:
print("App Cancel")
self.touchIdSwitch.setOn(true, animated: true)
break
case .SystemCancel:
print("System Cancel")
self.touchIdSwitch.setOn(true, animated: true)
break
case .UserCancel:
print("User Cancel")
dispatch_async(dispatch_get_main_queue()) {
self.touchIdSwitch.setOn(true, animated: false)
}
break
default:
break
}
} else {
print("no error")
}
}
} else {
AuthenticationSetter.setAuthenticationSwitchUserDefault(true)
}
}
and here is the AuthenticationSetter.touchIdAuthentication function
class func touchIdAuthentication(doAfterAuthentication: (Bool, NSError?) -> Void) {
let laContext = LAContext()
var error: NSError?
if laContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
laContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "TouchID Authentication", reply: doAfterAuthentication)
} else {
print("device don't support touchId")
}
}
and when I touch cancel at the touchid window, the "User Cancel" is printed but the switch doesn't become on.
I create a new demoViewController that has a UISwitch and a button
when the buttonpressed this function is called : demoSwitch.setOn(demoSwitch.on, true)
and It run just well!!!! can any ios master help me!

Related

google sign in sign in flow canceled iOS

I'm writing an iOS app that has to use google sign in. It compiles fine but when it runs and I try to sign in with a button, the sign in flow is interrupted:
This is the error: Optional("The operation couldn’t be completed. (com.google.GIDSignIn error -4.)")
error with signing in: Optional(Error Domain=com.google.GIDSignIn Code=-5 "The user canceled the sign-in flow." UserInfo={NSLocalizedDescription=The user canceled the sign-in flow.})
In the simulator, when I press the button, it will show the dialog box asking whether it is ok for the app to access google and then it will just disappear after a second or two.
Here is my ViewController:
//
// ViewController.swift
// frcscout
//
// Created by Elliot Scher on 12/20/22..
//
import UIKit
import GoogleSignIn
import GTMSessionFetcher
import GoogleAPIClientForREST
class ViewController: UIViewController {
#IBOutlet weak var signInButton: UIButton!
private let service = GTLRSheetsService()
override func viewDidLoad() {
super.viewDidLoad()
googleSignIn { signInStatus in
if signInStatus == true {
self.signInButton?.setTitle("Sign out", for: .normal)
print("Signed in!")
} else {
self.signInButton?.setTitle("Sign in", for: .normal)
print("Issues with signing in...")
}
}
}
#IBAction func signInPressed(_ sender: UIButton) {
print("Sign in pressed")
let user = GIDSignIn.sharedInstance.currentUser
if (user == nil) {
googleSignIn() { success in
if success == true {
sender.setTitle("Sign out", for: UIControl.State.normal)
} else {
return
}
}
} else {
GIDSignIn.sharedInstance.signOut()
self.service.authorizer = nil
sender.setTitle("Sign in", for: UIControl.State.normal)
}
}
}
extension ViewController {
func googleSignIn(completionHandler: #escaping (Bool) -> Void) {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error == nil {
print("Managed to restore previous sign in!\nScopes: \(String(describing: user?.grantedScopes))")
self.requestScopes(googleUser: user!) { success in
if success == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("No previous user!\nThis is the error: \(String(describing: error?.localizedDescription))")
let signInConfig = GIDConfiguration.init(clientID: K.clientID)
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { gUser, signInError in
if signInError == nil {
self.requestScopes(googleUser: gUser!) { signInSuccess in
if signInSuccess == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("error with signing in: \(String(describing: signInError)) ")
self.service.authorizer = nil
completionHandler(false)
}
}
}
}
}
func requestScopes(googleUser: GIDGoogleUser, completionHandler: #escaping (Bool) -> Void) {
let grantedScopes = googleUser.grantedScopes
if grantedScopes == nil || !grantedScopes!.contains(K.grantedScopes) {
let additionalScopes = K.additionalScopes
GIDSignIn.sharedInstance.addScopes(additionalScopes, presenting: self) { user, scopeError in
if scopeError == nil {
user?.authentication.do { authentication, err in
if err == nil {
guard let authentication = authentication else { return }
// Get the access token to attach it to a REST or gRPC request.
// let accessToken = authentication.accessToken
let authorizer = authentication.fetcherAuthorizer()
self.service.authorizer = authorizer
completionHandler(true)
} else {
print("Error with auth: \(String(describing: err?.localizedDescription))")
completionHandler(false)
}
}
} else {
completionHandler(false)
print("Error with adding scopes: \(String(describing: scopeError?.localizedDescription))")
}
}
} else {
print("Already contains the scopes!")
completionHandler(true)
}
}
}
Can anyone help me fix this? Thanks!!

Swift Segue - while an existing transition or presentation is occurring; the navigation stack will not be updated

The segue works fine when triggered outside the updatePassword func.
Full error -
'2020-07-03 13:17:34.656198+0100 FindingTaylorSwift[5305:154832] pushViewController:animated: called on <UINavigationController 0x7fc6a2037600> while an existing transition or presentation is occurring; the navigation stack will not be updated.'
#IBAction func updatePasword(_ sender: UIButton) {
.....
_ = self.awsUserPoolUpdatePassword.updatePasswordWithConfirmationCodeError?
.subscribe({ errorText in
guard let elementContent = errorText.element?.localizedDescription else { return }
switch elementContent {
case "The operation couldn’t be completed. (AWSMobileClient.AWSMobileClientError error 2.)":
self.showAlert(title: UpdatePasswordError.titleCode, message: UpdatePasswordError.messageCode)
case "The operation couldn’t be completed. (AWSMobileClient.AWSMobileClientError error 8.)":
self.showAlert(title: UpdatePasswordError.titlePassword, message: UpdatePasswordError.messagePasswordShort)
case "The operation couldn’t be completed. (AWSMobileClient.AWSMobileClientError error 9.)":
self.showAlert(title: UpdatePasswordError.titlePassword, message: UpdatePasswordError.messagePasswordComplexity)
default:
self.showAlert(title: UpdatePasswordError.titleDefault, message: UpdatePasswordError.messageDefault)
}
})
_ = self.awsUserPoolUpdatePassword.updatePasswordWithConfirmationCodeResult?
.subscribe({ resultText in
guard let elementContent = resultText.element?.forgotPasswordState else { return }
switch elementContent {
case .done:
self.showAlert(title: PasswordUpated.title, message: "")
self.transitionToLogin()
default:
self.showAlert(title: UpdatePasswordError.titleDefault, message: UpdatePasswordError.messageDefault)
}
})
}
private func transitionToLogin() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.performSegue(withIdentifier: AWSControllers.awsUpdatedPasswordSuccess, sender: self)
}
// DispatchQueue.main.async {
// self.performSegue(withIdentifier: AWSControllers.awsUpdatedPasswordSuccess, sender: self)
// }
}
AWS Class
internal func updatePasswordWithConfirmationCode(newPassword: String, confirmationCode: String) {
guard let username = self.awsUserNameEmail else { return }
let result = AWSMobileClient.default().rx.confirmForgotPassword(username: username, newPassword: newPassword, confirmationCode: confirmationCode)
.materialize()
result
.compactMap { $0.element }
.subscribe(onNext: { forgotPasswordResult in
switch forgotPasswordResult.forgotPasswordState {
case .done:
print("Password changed successfully")
self.userPasswordUpdateStatus = .passwordUpdateSuccessful
default:
print("Error: Could not change password.")
}
})
.disposed(by: disposeBag)
updatePasswordWithConfirmationCodeError = result.compactMap { $0.error }
updatePasswordWithConfirmationCodeResult = result.compactMap { $0.element }
}
}
extension Reactive where Base: AWSMobileClient {
func signIn(username: String, password: String) -> Observable<SignInResult> {
return Observable.create { observer in
self.base.signIn(username: username, password: password) { (signinResult, error) in
if let signinResult = signinResult {
observer.onNext(signinResult)
observer.onCompleted()
} else {
observer.onError(error ?? RxError.unknown)
}
}
return Disposables.create()
}
}
func confirmForgotPassword(username: String, newPassword: String, confirmationCode: String) -> Observable<ForgotPasswordResult> {
return Observable.create { observer in
self.base.confirmForgotPassword(username: username, newPassword: newPassword, confirmationCode: confirmationCode) { (confirmForgotPasswordResult, error) in
if let confirmForgotPasswordResult = confirmForgotPasswordResult {
observer.onNext(confirmForgotPasswordResult)
observer.onCompleted()
} else {
observer.onError(error ?? RxError.unknown)
}
}
return Disposables.create()
}
}
}
The segue should only be triggered after a successful password update, the password updates successful which is confirmed by a print statement.
I'm going to guess that showAlert presents a UIAlertController, which is a kind of UIViewController...
If my guess is correct, then your problem is with these lines:
self.showAlert(title: PasswordUpated.title, message: "")
self.transitionToLogin()
You are trying to present two view controllers at the same time. You need to wait until the alert is finished presenting before attempting to present the next view controller, or more likely wait until the alert is finished being dismissed.
(This is not an RxSwift question.)

iOS Local Biometric authentication dialogue called again and again

Below is my code. When ever I run this code and validate the app using TouchID, TouchID Authentication dialog is dismissed and viewDidLoad() is called again which in turn shows the TouchID alert again. So I am not able to leave this page and stuck in a loop. Any help would be appreciated.
Note: Same code was working fine 2 days ago.
override func viewDidLoad() {
super.viewDidLoad()
initialSetup()
checkAuthenticationMethod()
}
private func checkAuthenticationMethod() {
let biometricsEnabled = UserDefaults.standard.bool(forKey: LocalDefaults.biometricsEnabled.rawValue)
if biometricsEnabled {
OperationQueue.main.addOperation {
self.setupLocalAuthentication()
}
}
}
private func setupLocalAuthentication() {
var error: NSError?
context.localizedCancelTitle = "Login using your PIN"
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
if let err = error {
self.showAlert(message: err.localizedDescription, withTitle: "Error", willViewPop: false)
}else {
self.localAuthenticationMessage.isHidden = false
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Unlock App") { (success, error) in
DispatchQueue.main.async { [weak self] in
self?.view.isUserInteractionEnabled = !success
if success {
self?.showHUDLoader(containerView: nil,message: "Logging in", enableInteraction: false)
self?.pinImage.image = UIImage(named: "PIN_4")
self?.updateUserLoginToken()
}else if let err = error {
self?.pinImage.image = UIImage(named: "PIN_0")
let errorCode = (err as NSError).code
if errorCode != -4 {
self?.localAuthenticationMessage.isHidden = true
}
}
}
}
}
}
}

Updating a UILabel from an escaping closure doesn't take effect immedialtely

Basically:
In viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myFunction(closure)
}
closureis of type #escaping (_ success: Bool) -> Void
In the closure code:
print("Change myLabel.text")
self?.myLabel.text = "New Title"
print("myLabel.text changed")
"Show myLabel.text" and "myLabel.text changed" are printed as soon as the VC appears, but the text in myLabel changes after several seconds (around 10 seconds).
myLabel is created programmatically as seen below:
class MyClass : UIViewController {
...
var myLabel: UILabel!
var contacts = [ContactEntry]()
...
override func viewWillLayoutSubviews() {
myLabel = UILabel()
myLabel.text = "Original title"
myLabel.frame = CGRect(x: 10, y: 10, width: 100, height: 400)
self.view.addSubview(myLabel)
}
}
The actual code is inspired from here:
viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
requestAccessToContacts { [weak self] (success) in
if success {
self?.retrieveContacts(completion: { (success, contacts) in
self?.tableView.isHidden = false
self?.myLabel.isHidden = true
if success && (contacts?.count)! > 0 {
self?.contacts = contacts!
self?.myLabel.text = ""
self?.myLabel.isHidden = true
self?.tableView.reloadData()
} else if (contacts?.count)! == 0 {
self?.myLabel.isHidden = false
self?.myLabel.text = "No contacts found"
} else {{
self?.myLabel.isHidden = false
self?.myLabel.text = "Error loading contacts"
}
})
} else {
print("Change label text")
self?.myLabel.attributedText = "Enable access to contacts by going to\nSettings>Privacy>Contacts>MyApp"
self?.myLabel.isHidden = false
print("Label text changed")
}
}
}
requestAccessToContacts:
func requestAccessToContacts(completion: #escaping (_ success: Bool) -> Void) {
let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts)
switch authorizationStatus {
case .authorized:
// authorized previously
completion(true)
case .denied, .notDetermined:
// needs to ask for authorization
self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (accessGranted, error) -> Void in
completion(accessGranted)
})
default:
// not authorized.
completion(false)
}
}
retrieveContacts:
func retrieveContacts(completion: (_ success: Bool, _ contacts: [ContactEntry]?) -> Void) {
var contacts = [ContactEntry]()
do {
let contactsFetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactImageDataKey, CNContactImageDataAvailableKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey].map {$0 as CNKeyDescriptor})
try contactStore.enumerateContacts(with: contactsFetchRequest, usingBlock: { (cnContact, error) in
if let contact = ContactEntry(cnContact: cnContact) { contacts.append(contact) }
})
completion(true, contacts)
} catch {
completion(false, nil)
}
}
What am I missing here?
You are saying:
print("Change myLabel.text")
self?.myLabel.text = "New Title"
print("myLabel.text changed")
And you are complaining that the print messages appear in the console but the label doesn't change until much later.
This sort of delay is nearly always caused by a threading issue. You do not show MyFunction and you do not show the entirety of closure, so it's impossible to help you in detail, but the likelihood is that you are messing around with background threads without knowing what you are doing, and that you have accidentally set myLabel.text on a background thread, which is a big no-no. You must step out to the main thread in order to touch the interface in any way:
DispatchQueue.main.async {
print("Change myLabel.text")
self?.myLabel.text = "New Title"
print("myLabel.text changed")
// ... and everything else that touches the interface
}

Game Center log-in window never appearing

The problem here is that the player is not getting authenticated and I'm not getting the log-in window for game center so I really need to get this fixed as soon as possible.
func authenticateLocalPlayer() {
var localPLayer = GKLocalPlayer.localPlayer()
localPLayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
if viewController != nil {
self.presentViewController(viewController, animated: true, completion: nil)
} else {
if localPLayer.authenticated {
self.gameCenterEnabled = true
localPLayer.loadDefaultLeaderboardIdentifierWithCompletionHandler({ (leaderboardIdentifier : String!, error : NSError!) -> Void in
if error != nil {
println(error.localizedDescription)
} else {
self.leaderboardIdentifier = leaderboardIdentifier
}
})
} else {
self.gameCenterEnabled = false
}
}
}
}

Resources