Post

Replies

Boosts

Views

Activity

App Auth: error Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior
I'm using AppAuth pod to handle user login with Azure in my app. I followed this sample : https://github.com/openid/AppAuth-iOS/tree/master/Examples which works fine until my authentication code expires. It works ok for the 1st connection and all the time while the authenticationCode is still valid. Once it expires, I briefly see the alert to "Sign in" and then it disappears and I get the error :"Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior". (It works fine again if I delete the app and re-install it.) I read that I should "Ensure that there is a strong reference to the SFAuthenticationSession instance when the session is in progress.". And I think that's the case with the currentFlow declared in AppDelegate. (see code below) Did anyone ever faced and solved this issue ?import UIKit import AppAuth import AuthenticationServices var isLoginViewOn: Bool = false var isConnectionBtnPressed: Bool = false class ContainerController: UIViewController { // MARKS : Properties private var authState: OIDAuthState? var token: String? var menuController: MenuController! var homeController: HomeController! var panView: UIView! var isExpanded = false var isLoginOut: Bool = false let loginView: UIImageView = { let v = UIImageView() v.image = UIImage(named: "") v.contentMode = .scaleAspectFit return v }() let connexionButton: UIButton = { let b = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50)) return b }() let logoutBtn: UIButton = { let b = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50)) b.setTitle("déconnexion", for: .normal) return b }() override func viewDidLoad() { super.viewDidLoad() chekToken() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if isLoginViewOn == true { if isConnectionBtnPressed == false { self.connexionButton.sendActions(for: .touchUpInside) isLoginViewOn = false } } } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() view.layoutIfNeeded() view.layoutSubviews() } ///////////////////////////////////////////////////////////////////////////// /// SET UP //// ///////////////////////////////////////////////////////////////////////////// func setupLoginView() { print("setup loginView") isLoginViewOn = true view.addSubview(loginView) view.addSubview(connexionButton) loginView.translatesAutoresizingMaskIntoConstraints = false connexionButton.translatesAutoresizingMaskIntoConstraints = false [ loginView.centerYAnchor.constraint(equalTo: view.centerYAnchor), loginView.centerXAnchor.constraint(equalTo: view.centerXAnchor), loginView.widthAnchor.constraint(equalToConstant: 250), loginView.heightAnchor.constraint(equalToConstant: 128), connexionButton.bottomAnchor.constraint(equalTo: loginView.topAnchor, constant: -50), connexionButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), connexionButton.widthAnchor.constraint(equalToConstant: 200), connexionButton.heightAnchor.constraint(equalToConstant: 50), ].forEach{$0.isActive = true } connexionButton.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside) if isConnectionBtnPressed == false { self.connexionButton.sendActions(for: .touchUpInside) } } func setupHomeController() { homeController = HomeController() homeController.delegate = self addControllerAsChild(forController: homeController) setupPanView(forController: homeController) } }The login part :extension ContainerController { @objc func buttonAction(_ sender: UIButton){ self.login() isConnectionBtnPressed = true } func checkToken() { guard let data = UserDefaults.standard.object(forKey: kAppAuthExampleAuthStateKey) as? Data else { setupLoginView() return } do { let authState = try NSKeyedUnarchiver.unarchivedObject(ofClass: OIDAuthState.self, from: data) self.setAuthState(authState) self.getUserInfo() } catch { print("catch loadState: \(error)") } } func login() { print("Got configuration: \(config)") if let clientId = kClientID { self.doAuthWithAutoCodeExchange(configuration: config, clientID: clientId, clientSecret: nil) } } func doAuthWithAutoCodeExchange(configuration: OIDServiceConfiguration, clientID: String, clientSecret: String?) { guard let redirectURI = URL(string: kRedirectURI) else { print("Error creating URL for : \(kRedirectURI)") return } guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { print("Error accessing AppDelegate") return } // builds authentication request let request = OIDAuthorizationRequest(configuration: configuration, clientId: clientID, clientSecret: clientSecret, scopes: [OIDScopeOpenID, OIDScopeProfile, "--kclientID--", "offline_access"], redirectURL: redirectURI, responseType: OIDResponseTypeCode, additionalParameters: ["p": "b2c_1_my_app_sign_in_up"]) // performs authentication request print("Initiating authorization request with scope: \(request.scope ?? "DEFAULT_SCOPE")") appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in if let authState = authState { self.setAuthState(authState) print("Got authorization tokens. Access token") self.getUserInfo() } else { self.setAuthState(nil) print("Authorization error: \(error?.localizedDescription ?? "DEFAULT_ERROR")") } } } func getUserInfo() { let currentAccessToken: String? = self.authState?.lastTokenResponse?.accessToken self.authState?.performAction() { (accessToken, idToken, error) in if error != nil { CoreDataHelper().deleteIDToken("idToken") AuthenticationService().login() print("Error fetching fresh tokens: \(error?.localizedDescription ?? "ERROR")") return } guard let accessToken = accessToken else { print("Error getting accessToken") return } if currentAccessToken != accessToken { print("Access token was refreshed automatically ") self.token = currentAccessToken } else { print("Access token was fresh and not updated ") self.token = accessToken } self.loginView.removeFromSuperview() self.setupHomeController() } } func saveState() { var data: Data? = nil if let authState = self.authState { do { data = try NSKeyedArchiver.archivedData(withRootObject: authState, requiringSecureCoding: false) } catch { print("catch saveState: \(error)") } } UserDefaults.standard.set(data, forKey: kAppAuthExampleAuthStateKey) UserDefaults.standard.synchronize() } func setAuthState(_ authState: OIDAuthState?) { if (self.authState == authState) { return; } self.authState = authState; self.authState?.stateChangeDelegate = self; self.saveState() } } extension ContainerController: ASWebAuthenticationPresentationContextProviding { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { return view.window! }And AppDelegate above the class declaration :let kClientID: String? = "my client id"; let kRedirectURI: String = "myapp.test.authent://oauth/redirect"; let kAppAuthExampleAuthStateKey: String = "authState"; let authorizationEndpoint = URL(string: "https://login.microsoftonline.com/myapp.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_myaapp_sign_in_up")! let tokenEndpoint = URL(string: "https://login.microsoftonline.com/myapp.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_myaapp_sign_in_up")! let config = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint, tokenEndpoint: tokenEndpoint)And in the class : var currentAuthorizationFlow: OIDExternalUserAgentSession? func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { // Sends the URL to the current authorization flow (if any) which will // process it if it relates to an authorization response. if let authorizationFlow = self.currentAuthorizationFlow, authorizationFlow.resumeExternalUserAgentFlow(with: url) { self.currentAuthorizationFlow = nil return true } return false }Any suggestion welcomed 🙂
6
0
4.3k
Apr ’16