Clone App/Twitter

[Twitter Clone] Add ViewModel and bind view

밤새는 탐험가89 2024. 6. 5. 14:44

 

 

🟨 구현 화면

 

 

🟨 구현 순서

  • RegisterViewModel 파일 생성
    • 로그인에 필요한 프로퍼티, 메서드 생성
  • AuthManager 파일 생성 
    • 계정을 생성하는 메서드 생성 
  • RegisterViewController 
    • 텍스트필드에 입력하는 값을 RegisterViewModel로 전달하여 계정 생성 및 로그인

 

🟨 RegisterViewModel.swift

import Foundation
import Firebase
import FirebaseAuthCombineSwift
import Combine

final class RegisterViewViewModel: ObservableObject {
    
    
    @Published var email: String?
    @Published var password: String?
    @Published var isRegisterationFormValid: Bool = false
    @Published var user: User?
    
    private var subscriptions: Set<AnyCancellable> = []
    
    func validateRegistrationForm() {
        guard let email = email,
              let password = password else {
            isRegisterationFormValid = false
            return
        }
        isRegisterationFormValid = isValidEmail(email) && password.count >= 8 
    }
    
    func isValidEmail(_ email: String) -> Bool {
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"

        let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
        return emailPred.evaluate(with: email)
    }
    
    func createUser() {
        guard let email = email,
              let password = password else { return }
        
        AuthManager.shared.registerUser(with: email, password: password)
            .sink { _ in

            } receiveValue: { [weak self] user in
                self?.user = user
            }
            .store(in: &subscriptions)

    }
    
}

 

 

🟨 AuthManger.swift

import Foundation
import Firebase
import FirebaseAuthCombineSwift
import Combine



class AuthManager {
    
    static let shared = AuthManager()
    
    
    func registerUser(with email: String, password: String) -> AnyPublisher<User, Error> {
     
        return Auth.auth().createUser(withEmail: email, password: password)
            .map(\.user)
            .eraseToAnyPublisher()
    }
}

 

 

🟨 RegisterViewController.swift

import UIKit
import Combine

class RegisterViewController: UIViewController {
    
    // ViewModel
    private var viewModel = RegisterViewViewModel()
    private var subscriptions: Set<AnyCancellable> = []
    
    ...
    
    private let registerButton: UIButton = {
        
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Register", for: .normal)
        button.tintColor = .label
        button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
        button.backgroundColor = .systemCyan
        button.layer.masksToBounds = true
        button.layer.cornerRadius = 25
        button.isEnabled = false
        return button
    }()
    
    
    
    @objc private func didChangeEmailTextField() {
        viewModel.email = emailTextField.text
        viewModel.validateRegistrationForm()
    }
    
    @objc private func didChangePasswordTextField() {
        viewModel.password = passwordTextField.text
        viewModel.validateRegistrationForm()
    }
    
    // email, password 설정 함수
    private func bindViews() {
        emailTextField.addTarget(self, action: #selector(didChangeEmailTextField), for: .editingChanged)
        passwordTextField.addTarget(self, action: #selector(didChangePasswordTextField), for: .editingChanged)
        
        viewModel.$isRegisterationFormValid.sink { [weak self] validationState in
            self?.registerButton.isEnabled = validationState
        }
        .store(in: &subscriptions)
        
        viewModel.$user.sink { [weak self] user in
            print(user)
        }
        .store(in: &subscriptions)
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ... 
        
        registerButton.addTarget(self, action: #selector(didTapRegister), for: .touchUpInside)
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapToDismiss)))
        
        bindViews()
    }
    
    // textField 작성이 끝나고 빈 곳이나 enter 키를 누르면 키보드 내려간다.
    @objc private func didTapToDismiss() {
        view.endEditing(true)
    }
    
    
    @objc private func didTapRegister() {
        viewModel.createUser()
    }
    
    ...
}

 

 

🟨 TIL

  • MVVM 패턴 
  • Firebase를 통해 계정 생성 및 로그인 구현