ForgeRock Developer Experience

Step 9. Collect the username and password

To handle the Callback objects

  1. Implement the UI controls that collect the username and password. For each callback that is received, display a UIAlertController to collect the required information. In this case we require only the nameCallback and the passwordCallback.

    For demonstration purposes, this example uses a simple UIAlertController to collect the username and password, rather than implementing a custom UI login screen:

    else if let node = node {
        print("Node object received, handle the node")
        DispatchQueue.main.async {
            let alert = UIAlertController(title: "User Authentication", message: nil, preferredStyle: .alert)
            for callback: Callback in node.callbacks {
                if callback.type == "NameCallback", let nameCallback = callback as?
                    NameCallback {
                    alert.addTextField { (textField) in
                        textField.placeholder = nameCallback.prompt
                        textField.autocorrectionType = .no
                        textField.autocapitalizationType = .none
                    }
    
                }
                else if callback.type == "PasswordCallback",
                        let passwordCallback = callback as? PasswordCallback {
                    alert.addTextField { (textField) in
                        textField.placeholder = passwordCallback.prompt
                        textField.autocorrectionType = .no
                        textField.autocapitalizationType = .none
                    }
                }
            }
    
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let submitAction = UIAlertAction(title: "Next", style: .default) { (_) in
                var index = 0
                for textField in alert.textFields! {
                    let thisCallback: SingleValueCallback = node.callbacks[index] as! SingleValueCallback
                    thisCallback.setValue(textField.text)
                    index += 1
                }
                node.next { (user: FRUser?, node, error) in
                    self.handleNode(user: user, node: node, error: error)
                }
            }
    
            alert.addAction(cancelAction)
            alert.addAction(submitAction)
    
            self.present(alert, animated: true, completion: nil)
        }
    }
  2. Run the application.

  3. When you click the Login button, the UI alert displays the text fields for collecting the username and password:

    callback alerts
  4. To submit the content of the text fields to the SDK, click the Next button.

    The SDK sends the information to the server:

    • The submitAction retrieves the callback from the node to ensure that the values in the text fields can be input into the callback.

      Note that this code uses a SingleValueCallback, because both the NameCallback and the PasswordCallback inherit from the SingleValueCallback.

    • In the callback, set the value of the callback to the value of the text field.

    • Increment the value to iterate through the text fields.

    • Move to the next node, and pass the process to the handleNode() method.

    Your complete ViewController.swift should now resemble the following:

    //
    //  ViewController.swift
    //  DemoApp
    //
    
    import UIKit
    import FRAuth
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var statusLabel: UILabel?
        @IBOutlet weak var loginButton: UIButton?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            FRLog.setLogLevel([.error, .network])
    
            do {
                try FRAuth.start()
                print ("SDK initialized successfully")
            } catch {
                print (error)
            }
    
            updateStatus()
        }
    
        func updateStatus() {
            if let _ = FRUser.currentUser {
                statusLabel?.text = "User is authenticated"
            }
            else {
                statusLabel?.text = "User is not authenticated"
            }
        }
    
        func handleNode(user: FRUser?, node: Node?, error: Error?) {
            if let _ = user {
                print("User is authenticated")
    
                DispatchQueue.main.async {
                    self.updateStatus()
                }
            }
            else if let node = node {
                print("Node object received, handle the node")
                DispatchQueue.main.async {
                    let alert = UIAlertController(title: "User Authentication", message: nil, preferredStyle: .alert)
                    for callback: Callback in node.callbacks {
                        if callback.type == "NameCallback", let nameCallback = callback as?
                            NameCallback {
                            alert.addTextField { (textField) in
                                textField.placeholder = nameCallback.prompt
                                textField.autocorrectionType = .no
                                textField.autocapitalizationType = .none
                            }
    
                        }
                        else if callback.type == "PasswordCallback",
                                let passwordCallback = callback as? PasswordCallback {
                            alert.addTextField { (textField) in
                                textField.placeholder = passwordCallback.prompt
                                textField.autocorrectionType = .no
                                textField.autocapitalizationType = .none
                            }
                        }
                    }
    
                    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
                    let submitAction = UIAlertAction(title: "Next", style: .default) { (_) in
                        var index = 0
                        for textField in alert.textFields! {
                            let thisCallback: SingleValueCallback = node.callbacks[index] as! SingleValueCallback
                            thisCallback.setValue(textField.text)
                            index += 1
                        }
                        node.next { (user: FRUser?, node, error) in
                            self.handleNode(user: user, node: node, error: error)
                        }
                    }
    
                    alert.addAction(cancelAction)
                    alert.addAction(submitAction)
    
                    self.present(alert, animated: true, completion: nil)
                }
            }
            else {
                print ("Something went wrong: \(String(describing: error))")
            }
        }
    
        @IBAction func loginButtonPressed(sender: UIButton) {
            print("Login button is pressed")
    
            FRUser.login {(user: FRUser?, node, error) in
                self.handleNode(user: user, node: node, error: error)
            }
        }
    
    }