Skip to content

Instantly share code, notes, and snippets.

@LukeSmith16
Last active August 31, 2019 22:49
Show Gist options
  • Save LukeSmith16/8453cd253234628a1084815c4c5a4ad2 to your computer and use it in GitHub Desktop.
Save LukeSmith16/8453cd253234628a1084815c4c5a4ad2 to your computer and use it in GitHub Desktop.
LoginViewModel - An example of how a LoginViewModel might look like for a login module, including: validation, talking to a repository, coordinator and view (via delegation).
import Foundation
protocol LoginViewModelInterface {
var viewDelegate: LoginViewModelDelegate? {get set}
func handleLogin(username: String, password: String)
}
/// You're coordinator implements this
protocol LoginViewModelCoordinatorDelegate: class {
func goToLoginSuccessful()
func goToSignup()
}
/// You're view module implements this
protocol LoginViewModelDelegate: class {
func showAlert(title: String?, message: String?)
}
enum LoginEntryValidation: Error {
case UsernameEmpty
case UsernameTooShort
case PasswordEmpty
case PasswordTooShort
var localizedDescription: String {
switch self {
case .UsernameEmpty:
return "Please enter a username"
case .UsernameTooShort:
return "Your username must be greater than 5 characters"
case .PasswordEmpty:
return "Please enter a password"
case .PasswordTooShort:
return "Your password must be greater than 5 characters"
}
}
}
class LoginViewModel: LoginViewModelInterface {
weak var coordinatorDelegate: LoginViewModelCoordinatorDelegate?
weak var viewDelegate: LoginViewModelDelegate?
private let repository: LoginRepositoryInterface!
init(repository: LoginRepositoryInterface = RemoteLoginRepository()) {
self.repository = repository
}
func handleLogin(username: String, password: String) {
guard isUserCredentialsValid(username: username, password: password) else { return }
repository.authenticate(username: username, password: password) { [weak self] (result) in
guard let `self` = self else { return }
switch result {
case .success(let authToken):
self.repository.saveAuthToken(authToken)
case .failure(let error):
self.viewDelegate?.showAlert(title: "Login Error", message: error.localizedDescription)
}
}
}
}
private extension LoginViewModel {
func isUserCredentialsValid(username: String, password: String) -> Bool {
do {
try validateUsername(username)
try validatePassword(password)
return true
} catch (let error as LoginEntryValidation) {
viewDelegate?.showAlert(title: "Login Error", message: error.localizedDescription)
} catch {
fatalError("Unknown error occurred")
}
return false
}
func validateUsername(_ username: String) throws {
guard username != "" else {
throw LoginEntryValidation.UsernameEmpty
}
guard username.count >= 5 else {
throw LoginEntryValidation.UsernameTooShort
}
}
func validatePassword(_ password: String) throws {
guard password != "" else {
throw LoginEntryValidation.PasswordEmpty
}
guard password.count >= 5 else {
throw LoginEntryValidation.PasswordTooShort
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment