Donate. I desperately need donations to survive due to my health

Get paid by answering surveys Click here

Click here to donate

Remote/Work from Home jobs

My MVVM implementation code does not work:

I am trying to implement MVVM-C design pattern with Firebase Authentication using Mobile phone.

For testing the authentication process I created a single-view UI for sending mobile number (view for country code & mobile number combined in a single string) and second-view for submitting OTP (a stack-view with six-text-fields in a stack) all on a single scene and it works using a single viewController.

However, to improve the look and feel. I have separated UI for entering mobile number and OTP on two seperate viewControllers and the verification process has stoped working.

First view is successfully sending the mobile-number to Firebase and receiving the OTP.

So I assume that RequestCodeViewController.swift is working perfectly as I am receiving the OTP.

However, when I enter that received "OTP" in the second view VerifyMobileViewController.swift (using TextFieldStackView) and press the done button (verify OTP) to authenticate then there is nothing happening. "Done" button action does nothing.

I tried to debug by adding several error messages at different stages of the button action but failed... (tried two days...sorry, am a beginner)

I believe as per my limited Swift knowledge that it is somehow not sending the full OTP in a single string or in one package...(I could be wrong)

ISSUE:Code does not work at "Done" button press (Button Action) on the VerifyMobileViewController.swift

I am receiving the OTP successfully. However, unable to verify mobile using the received OTP (VerifyMobileViewController.swift).

Two Seperate views Single UI on a single ViewController

My code is as follows:

Storyboard.swift

import UIKit

enum AppStoryboard : String {

    case Main, HelpPage, PrivacyStatement, Booking, JobHostOne, Login

    var instance : UIStoryboard {

        return UIStoryboard(name: self.rawValue, bundle: Bundle.main)
    }

    func viewController<T : UIViewController>(viewControllerClass : T.Type) -> T {

        let storyboardID = (viewControllerClass as UIViewController.Type).storyboardID

        guard let scene = instance.instantiateViewController(withIdentifier: storyboardID) as? T else {
            fatalError("ViewController with identifier \(storyboardID), not found in \(self.rawValue) Storyboard")
        }

        return scene
    }

    func initialViewController() -> UIViewController? {

        return instance.instantiateInitialViewController()
    }
}

extension UIViewController {

    // Not using static as it wont be possible to override to provide custom storyboardID then
    class var storyboardID : String {
        return "\(self)"
    }

    static func instantiate(fromAppStoryboard appStoryboard: AppStoryboard) -> Self {

        return appStoryboard.viewController(viewControllerClass: self)
    }
}

Cordinator.swift

import Foundation

class Coordinator {

    private(set) var childCoordinators: [Coordinator] = []

    func start() {
        preconditionFailure("This method needs to be overriden by concrete subclass.")
    }

    func finish() {
        preconditionFailure("This method needs to be overriden by concrete subclass.")
    }

    func addChildCoordinator(_ coordinator: Coordinator) {
        childCoordinators.append(coordinator)
    }

    func removeChildCoordinator(_ coordinator: Coordinator) {
        if let index = childCoordinators.index(where: { (element) -> Bool in
            return element == coordinator
        }) {
            childCoordinators.remove(at: index)
        }
        else {
            preconditionFailure("Couldn't remove coordinator: \(coordinator). It's not a child coordinator.")
        }
    }

    func removeAllChildCoordinatorsWith<T>(type: T.Type) {
        childCoordinators = childCoordinators.filter { $0 is T  == false }
    }

    func removeAllChildCoordinators() {
        childCoordinators.removeAll()
    }
}
extension Coordinator: Equatable {

    static func == (lhs: Coordinator, rhs: Coordinator) -> Bool {
        return lhs === rhs
    }
}

AppCoordinator.swift

import UIKit

class AppCoordinator: Coordinator {

    // MARK: - Properties
    var window: UIWindow

    // MARK: - Coordinator
    init(window: UIWindow?) {
        self.window = window!
    }

    override func start() {
        let loginCoordinator = LoginCoordinator(window: window)
        loginCoordinator.coordinatorDelegate = self
        loginCoordinator.start()

    }

    override func finish() {

    }
}

extension AppCoordinator: LoginCoordinatorDelegate {

}

Constants.swift

import Foundation

    //MARK:- UserDefaults Keys
    let authenticationIdKey = "authVerificationID"

    //MARK:- UIStoryBoard Indetifier
    let loginStroyBoard = "Login"

    //MARK:- UIViewController Indentifier

    let loginIndetifier = "RequestCodeViewController"

    //MARK: Error Messages
    let authenticationFailedMsg = "Authentication failed."
    let phonenumberNotValidMsg = "Please enter valid phone number."
    let verificationCodeEmptyMsg = "Validation code is empty."
    let verificationCodeNotValidMsg = "Validation code is wrong"

    //MARK: Success Messages
    let authenticationSuccessMsg = "Authentication successfull."

Utils.swift

import UIKit
    class Utils {
    static func showProgress() {
        SVProgressHUD.show()
    }
    static func dismissProgress() {
        SVProgressHUD.dismiss()
    }


static func showSingleAlert(message: String, controller: UIViewController) {
        let alertController = UIAlertController(title: "iGuess", message: message, preferredStyle: .alert)
        let okButton = UIAlertAction(title: "Ok", style: .default) { (action) in
            alertController.dismiss(animated: true, completion: nil)
        }
        alertController.addAction(okButton)
        controller.present(alertController, animated: true, completion: nil)
    }
}



extension String  {
    func trimText()-> String {
        return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    }
    subscript (i: Int) -> Character {
        return self[index(startIndex, offsetBy: i)]
    }
}

RequestCodeViewController.swift

import UIKit

import CoreTelephony



class RequestCodeViewController: UIViewController {



@IBOutlet weak var countryCodeTextField: UITextField!

@IBOutlet weak var phoneTextField: UITextField!



//MARK:- Properties

var viewModel: LoginViewModel? {

    willSet {

        viewModel?.viewDelegate = nil

    }

    didSet {

        viewModel?.viewDelegate = self

    }

}



override func viewDidLoad() {

    super.viewDidLoad()

    // Do any additional setup after loading the view.

    setUpUI()

    self.navigationItem.title = "iGuestBooking"

}



override func didReceiveMemoryWarning() {

    super.didReceiveMemoryWarning()

    // Dispose of any resources that can be recreated.

}

func setUpUI() {

    phoneTextField.addTarget(self, action:      #selector(textFieldDidChange(_:)), for: .editingChanged)

    countryCodeTextField.addTarget(self, action:     #selector(textFieldDidChange(_:)), for: .editingChanged)


    let networkInfo = CTTelephonyNetworkInfo()



    if let carrier = networkInfo.subscriberCellularProvider {

    self.viewModel?.countryCode = "+\(carrier.mobileCountryCode!)"

    }

    phoneTextField.text = self.viewModel?.phoneNumber

    countryCodeTextField.text = self.viewModel?.countryCode

}

//MARK:- Actions

@IBAction func sendVerificationButtonAction(_ sender: Any) {

    if self.phoneTextField.text!.trimText().isEmpty || self.countryCodeTextField.text!.trimText().isEmpty {

        Utils.showSingleAlert(message: phonenumberNotValidMsg, controller: self)

        return

    }



viewModel?.sendVerificationCode(callBack: { (isSuccess) -> (Void) in

        if isSuccess {

            //self.refreshCodeTextFields()

            let verifyMobileScene = VerifyMobileViewController.instantiate(fromAppStoryboard: .Login)

            self.present(verifyMobileScene, animated: true, completion: nil)

        }

        else {

            self.dismiss(animated: true, completion: nil)

        }

    })

}



 @IBAction func textFieldDidChange(_ sender: UITextField) {

        if sender == phoneTextField {

            viewModel?.phoneNumber = sender.text!

        }

        else {

            viewModel?.countryCode = sender.text!

        }

    }

    // MARK: - to dismiss the keyboard BEGIN

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        self.view.endEditing(true)

    }

    // MARK: - to dismiss the keyboard ENDS

}



    //MARK:- LoginViewModelViewDelgate


extension RequestCodeViewController: LoginViewModelViewDelegate {



}

VerifyMobileViewController.swift

import UIKit
import CoreTelephony

class VerifyMobileViewController: UIViewController {

    // MARK: - to dismiss the keyboard BEGIN
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }
// MARK: - to dismiss the keyboard ENDS

@IBOutlet weak var codeTextField: UITextField!
@IBOutlet weak var textFieldStackView: UIStackView!

@IBOutlet weak var doneButton: UIButton!

//MARK:- Properties
var viewModel: LoginViewModel? {
    willSet {
        viewModel?.viewDelegate = nil
    }
    didSet {
        viewModel?.viewDelegate = self
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    setUpUI()
    self.navigationItem.title = "iLogin"
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}
    func setUpUI() {
        codeTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        self.textFieldStackView.isHidden = false
        self.codeTextField.isHidden = false
        self.doneButton.isHidden = false
    }

    func refreshCodeTextFields() {
        self.codeTextField.text = ""
        for textField in textFieldStackView.subviews as! [UITextField] {
            textField.text = ""
//            textField.delegate = self
        }
    }

@IBAction func doneButtonAction(_ sender: Any) {
    var verificationCode: String = ""
    for textField in textFieldStackView.subviews as! [UITextField]{
        if (textField.text?.isEmpty)! {
            Utils.showSingleAlert(message: verificationCodeEmptyMsg, controller: self) //
            return
        }
        verificationCode.append(textField.text ?? "")
        print(textField.text ?? "") //
        //Utils.showSingleAlert(message: verificationCodeNotValidMsg, controller: self) //
    }
    viewModel?.verifyCode(code: verificationCode,callBack: {
        [weak self]
        (user, error) -> Void in
        guard let self_ = self else { return }
        if error == nil {
            print(user  ?? "") //
            self_.refreshCodeTextFields()
            self_.textFieldStackView.isHidden = false
            self_.codeTextField.isHidden = false
            Utils.showSingleAlert(message: authenticationSuccessMsg, controller: self_)
        }
        else {
            Utils.showSingleAlert(message: authenticationFailedMsg, controller: self_)
            print(error?.localizedDescription ?? "")
        }
    })
}
//


 @IBAction func textFieldDidChange(_ sender: UITextField) {
        if sender == codeTextField {
            for tampTextField in textFieldStackView.subviews as! [UITextField] {
                if sender.text!.count  > (tampTextField.tag - 1)  {
                    if let tamp = sender.text?.index(sender.text!.startIndex, offsetBy: tampTextField.tag - 1) {
                        tampTextField.text = String(describing: sender.text![tamp])
                    }
                }
                else {
                    tampTextField.text = ""
                }
            }
        }
    }
    //
}

//MARK:- UITextFieldDelegate

extension VerifyMobileViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if range.location > 5 {
            return false
        }
        return true
    }
}

//MARK:- LoginViewModelViewDelgate

extension VerifyMobileViewController: LoginViewModelViewDelegate {

}

I am an absolute beginner and just started learning Swift. Thanking you kindly all for all the help in advance.

Comments