Как обработать результат от Alamofire без разрушения шаблона MVVM - PullRequest
2 голосов
/ 04 июля 2019

Итак, я недавно начал переносить свой код в шаблон MVVM, и у меня возникли некоторые проблемы. В настоящее время я работаю над своим loginPage, который аутентифицирует пользователя. Проблема в том, что я вызываю свою функцию, но мой код не ждет ответа от нее и переходит на следующую строку. Я думаю, это из-за асинхронных вещей Аламофира. Вот мой код:

// My login view controller
class loginPageViewController: UIViewController, UITextFieldDelegate {
    private var viewModel = userViewModel()
    @IBOutlet weak var usernameTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    @IBAction func signInButton(_ sender: roundButton) {
        if(usernameTextField.text != "" && passwordTextField.text != "") {
            self.viewModel.validate(nameField: self.usernameTextField.text!, passField: self.passwordTextField.text!)
            if(self.viewModel.isDone){
                print("success")
                self.presentPage(identifier: "homePageNavCtrl")
            }
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        usernameTextField.delegate = self
        passwordTextField.delegate = self
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
        usernameTextField.resignFirstResponder()
        passwordTextField.resignFirstResponder()
        return true;
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }
}
private extension loginPageViewController{
    func displayErrorMessage(errorMessage: String){
        let alert = UIAlertController(title: "Error", message: errorMessage, preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
    func presentPage(identifier: String){
        let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
        let nextViewController = storyBoard.instantiateViewController(withIdentifier: identifier)
        self.present(nextViewController, animated:true, completion:nil)
    }
}

То, что я хочу сделать, - это когда нажата кнопка входа, подтвердить пользователя с помощью запроса JSON и перейти на следующую страницу, если учетные данные верны.

Вот мой userViewModel

class userViewModel{
    private var mng = restInterface()
    private var User = user()
    var isDone = false
    var id: Int {
        return User.ID
    }
    var username: String {
        return User.LOGINNAME
    }
    var password: String {
        return User.PASSHASH
    }
}

extension userViewModel{
    func updateId(id: Int){
        User.ID = id
    }
    func updateUsername(username: String){
        User.LOGINNAME = username
    }

    func updatePassword(password: String){
        User.PASSHASH = password
    }

    func validate(nameField: String, passField: String){
        let accountBaseURL = "myUrlLiesDownHere"
        var nm = nameField
        var ps = passField
        nm.insert("'", at: nm.startIndex)
        nm.insert("'", at: nm.endIndex)
        ps.insert("'", at: ps.startIndex)
        ps.insert("'", at: ps.endIndex)
        let params : [String: String] = ["sUser" : nm, "sPass" : ps, "sToken": "''"]
            self.mng.performRequest(URLString: accountBaseURL, parameters: params, method: .get, successCallback: { (dict) in
                self.User.ID = dict.value(forKey: "ID") as! Int
                self.User.LOGINNAME = dict.value(forKey: "LOGINNAME") as! String
                self.User.PASSHASH = dict.value(forKey: "PASSHASH") as! String
                self.loginDone()
            }) { (error) in
                print("Error")
        }
    }

    func loginDone(){
        self.isDone = true
    }
}

А вот и мой интерфейс отдыха

class restInterface {
    func performRequest(URLString: String, parameters : Parameters, method:HTTPMethod, successCallback: @escaping (NSDictionary) -> Void, errorCallBack: @escaping (String) -> Void) -> Void{

        Alamofire.request(URLString, method: method, parameters: parameters).responseJSON { response in
            print(response)
            if let JSON = response.result.value as? [String: Any] {
                print(JSON)
                successCallback(JSON as NSDictionary)
            } else {
                errorCallBack("JSON Doesn't Exist")
            }
        }
    }
}

Логическое значение isDone для userViewModel должно быть истинным, когда я управляю им в моем контроллере представления. Но это не так. Когда я нажимаю кнопку один раз, результат становится истинным, но ничего не происходит. Когда я нажимаю его снова, он перенаправляет меня на следующую страницу, которую я хочу. Так что работа с Async действительно смутила меня, и я ищу помощь здесь :) Спасибо ...

Ответы [ 2 ]

1 голос
/ 04 июля 2019

---- Изменение в методе проверки -

func validate(nameField: String, passField: String, completionHandler: @escaping (_: NSDictionary?, _: NSError?) -> Void){
        let accountBaseURL = "myUrlLiesDownHere"
        var nm = nameField
        var ps = passField
        nm.insert("'", at: nm.startIndex)
        nm.insert("'", at: nm.endIndex)
        ps.insert("'", at: ps.startIndex)
        ps.insert("'", at: ps.endIndex)
        let params : [String: String] = ["sUser" : nm, "sPass" : ps, "sToken": "''"]
            self.mng.performRequest(URLString: accountBaseURL, parameters: params, method: .get, successCallback: { (dict) in
                self.User.ID = dict.value(forKey: "ID") as! Int
                self.User.LOGINNAME = dict.value(forKey: "LOGINNAME") as! String
                self.User.PASSHASH = dict.value(forKey: "PASSHASH") as! String
                completionHandler(dict, nil)
            }) { (error) in
                print("Error")
                completionHandler(nil, error)
        }
    }

------ изменение в вызове метода -

@IBAction func signInButton(_ sender: roundButton) {
        if(usernameTextField.text != "" && passwordTextField.text != "") {
          self.viewModel.validate(nameField: self.usernameTextField.text!, passField: self.passwordTextField.text!) { (resultDict, error) in
            if error != nil {
              print("Error")
            } else {
              print("success")
              self.presentPage(identifier: "homePageNavCtrl")
            }
         }
     }
1 голос
/ 04 июля 2019

В вас UserViewModel создайте обратный вызов completionHandler и внедрите его внутри viewController, чтобы представить следующий viewController.

class UserViewModel {

    public var loginCompletion: (()-> Void)?

    private var mng = restInterface()
    private var User = user()

    // rest of your code

}

Вызов completionHandler после завершения запроса,

extension userViewModel{


    func validate(nameField: String, passField: String){
        let accountBaseURL = "myUrlLiesDownHere"
        var nm = nameField
        var ps = passField
        nm.insert("'", at: nm.startIndex)
        nm.insert("'", at: nm.endIndex)
        ps.insert("'", at: ps.startIndex)
        ps.insert("'", at: ps.endIndex)
        let params : [String: String] = ["sUser" : nm, "sPass" : ps, "sToken": "''"]
            self.mng.performRequest(URLString: accountBaseURL, parameters: params, method: .get, successCallback: { (dict) in
                self.User.ID = dict.value(forKey: "ID") as! Int
                self.User.LOGINNAME = dict.value(forKey: "LOGINNAME") as! String
                self.User.PASSHASH = dict.value(forKey: "PASSHASH") as! String
                self.loginDone()

                self.loginCompletion?()

            }) { (error) in
                print("Error")
        }
    }

    func loginDone(){
        self.isDone = true
    }
}

completionHandler использование внутри viewController,

   class loginPageViewController: UIViewController, UITextFieldDelegate {

        private var viewModel = userViewModel()
        @IBOutlet weak var usernameTextField: UITextField!
        @IBOutlet weak var passwordTextField: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            usernameTextField.delegate = self
            passwordTextField.delegate = self

            self.viewModel.loginCompletion = { [weak self] in
                print("success")
                self?.presentPage(identifier: "homePageNavCtrl")
            }
        }

        @IBAction func signInButton(_ sender: roundButton) {
            if(usernameTextField.text != "" && passwordTextField.text != "") {
                self.viewModel.validate(nameField: self.usernameTextField.text!, passField: self.passwordTextField.text!)
            }
        }

        // rest of your code
    }

Примечания: В Swift имя class/struct/enum должно начинаться с заглавной буквы. Вышеупомянутое решение является очень простой настройкой для наблюдения данных между view-viewModel. Вам следует искать гораздо лучшее решение для привязки данных, например, Rx-Swift , Bond , Двухстороннее связывание и т. Д.

...