---
# System prepended metadata

title: Untitled

---

```swift=
import RxSwift
import RxCocoa

enum AuthAction {
    case userLoggedIn
    case userLoggedOut
    case newUserCreated
}

class Analytics {
    static var shared = Analytics()
    
    func trackScreen(_ screenName: String) {
        //...
    }
    
    func trackActivity(_ userActivity: String) {
        //...
    }
}
class LoginService {
    func login(withEmail email: String, password: String, completion: @escaping (Bool, Error?) -> Void) {
        //...
    }
}

class AppServices {
    static let shared = AppServices()
    private init() {}
    
    var loginService = LoginService()
    

}

class ViewModel {
    let emailText = BehaviorSubject<String>(value: "")
    let passwordText = BehaviorSubject<String>(value: "")
    
    let errorSubject = PublishSubject<Error>()
    let loggedInSuccessfully = PublishSubject<Bool>()
    
    private let disposeBag = DisposeBag()
    
    private let services: AppServices
    
    init(services: AppServices = AppServices.shared) {
        self.services = services
    }
    
    func signIn() {
        Observable.combineLatest(emailText, passwordText)
            .subscribe(onNext: { [weak self] email, password in
                self?.services.loginService.login(withEmail: email, password: password) { [weak self] didLogin, error in
                    if let error = error {
                        self?.errorSubject.onNext(error)
                        return
                    }
                    self?.loggedInSuccessfully.onNext(didLogin)
                }
            }).disposed(by: disposeBag)
    }
    
}

class LoginViewController: UIViewController {
    
    private let emailTextField = UITextField()
    private let passwordTextFeld = UITextField()
    private let signInButton = UIButton()
    
    private let disposeBag = DisposeBag()
    
    private var viewModel: ViewModel!
    
    convenience init(viewModel: ViewModel) {
        self.init()
        self.viewModel = viewModel
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        Analytics.shared.trackScreen("\(Self.self) will appear")
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        Analytics.shared.trackScreen("\(Self.self) will disappear")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        emailTextField
            .rx.text
            .compactMap { $0 }
            .subscribe(onNext: { [unowned self] in
                viewModel.emailText.onNext($0)
            })
            .disposed(by: disposeBag)
        
        passwordTextFeld
            .rx.text
            .compactMap { $0 }
            .subscribe(onNext: { [unowned self] in
                viewModel.passwordText.onNext($0)
            })
            .disposed(by: disposeBag)
        
        signInButton
            .rx.tap
            .subscribe(onNext: { [unowned self] in
                viewModel.signIn()
            })
            .disposed(by: disposeBag)
        
        viewModel.errorSubject.subscribe(onNext: { error in
            // show alert with error
        }).disposed(by: disposeBag)
        
        viewModel.loggedInSuccessfully.subscribe(onNext: { didLogin in
            if didLogin {
                NotificationCenter.default.post(name: Notification.Name("USER_AUTHENTICATION"), object: AuthAction.userLoggedIn)
                Analytics.shared.trackActivity("user logged in")
            }
        })
        .disposed(by: disposeBag)
    }
}

class LogoutViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //some bindings and setup
        NotificationCenter.default.post(name: Notification.Name("USER_AUTHENTICATION"), object: AuthAction.userLoggedOut)
    }
}

class RegistrationViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //some bindings and setup
        NotificationCenter.default.post(name: Notification.Name("USER_AUTHENTICATION"), object: AuthAction.newUserCreated)
    }
}

```