I'm working on a Swift app's login process and I'd like to know if I should include both success and failure handlers to the function, which checks if the user has successfully logged in or not.
What I want to do here is trigger the successCompletion() block only when the user successfully logged in and otherwise display an Alert with a message. I feel the login function takes multiple arguments, and I think it is kind of hard to figure out it handles two completion handlers from LoginViewController. So, should I include both error and success handlers in the function or better to separate them somehow?
In my LoginViewController,
class LoginViewController: UIViewController {
private let loginView = LoginView()
@objc func loginButtonAction() {
let idInputValue = loginView.loginIdTextField.text!
let passInputValue = loginView.passwordTextField.text!
LoginAPI().login(with: idInputValue, and: passInputValue, errorCompletion: show(message:)) { _ in
let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as! SceneDelegate
sceneDelegate.setRootVC(to: HomeViewController())
}
func showAlert(message: String) {
let okAction = UIAlertAction(title: Alert.ButtonTitle.ok, style: .default, handler: nil)
showAlert(title: Alert.Title.loginError, message: message, actions: [okAction], style: .alert)
}
}
and my LoginAPI class,
import Alamofire
class LoginAPI {
static var accountInfo: AccountInfo?
private let loginEndpoint = "https://example.com"
func login(with id: String, and password: String, errorCompletion: @escaping (_ m: String) -> Void?, successCompletion: @escaping () -> Void) {
let parameter: [String: Any] = ["id": id, "password": password]
AF.request(loginEndpoint, method: .post, parameters: parameter)
.validate()
.responseJSON { response in
guard let data = response.data else {
errorCompletion(response.error)
return
}
let decoder: JSONDecoder = JSONDecoder()
var accountInfo: AccountInfo?
do {
accountInfo = try decoder.decode(AccountInfo.self, from: data)
} catch {
errorCompletion(error)
}
LoginAPI.accountInfo = accountInfo
DispatchQueue.main.async {
successCompletion()
}
}
}
}
updated the function
func login(with id: String, and password: String, completed: @escaping (Result<AccountInfo, LoginError>) -> Void) {
let parameter: [String: Any] = ["id": id, "password": password]
AF.request(loginEndpoint, method: .post, parameters: parameter)
.responseDecodable(of: AccountInfo.self) { response in
switch(response.result) {
case .success(let data):
completed(.success(data))
case .failure(let error):
completed(.failure(.test))
}
}
}
CodePudding user response:
It's common to use Result. By returning the result you keep the responsibility of login() tight and handle the result somewhere else. And it indeed also avoids the unclarity of two completion blocks.
Other remarks: You don't call errorCompletion() on the main thread.
