Update User Authentication

이세진·2022년 6월 24일
0

iOS

목록 보기
24/46

생성일: 2022년 1월 21일 오후 10:26

현재 필요한 개선 사항

  1. 기존에 로그인되어 있던 사람이 로그아웃하고 다른 계정으로 로그인을 했을 때 기본에 로그인되어있던 계정으로 접속이 됨
    • 이유
      • 기존 코드에서는 ProfileController.swift 의 fetchUser() 함수가 현재 사용자의 정보를 가져와서 보여주는 구조이고 로그아웃을 하면 LoginController가 화면 위를 덮는 구조이다 ⇒ 로그인에 성공(어느 계정이든 상관 x) 하면 화면을 덮고 있던 LoginController를 dismiss하는 형식 ⇒ 기존에 사용중이었던 계정 정보가 담긴 프로필 화면으로 되돌아감 ⇒ 다른 계정으로 로그인 불가
      • 동일한 문제가 로그인 뿐만 아니라 회원가입에도 적용된다.

해결 과정

  1. ProfileController.swift에 있던 fetchUser() 함수를 없애고 MainTabController.swift로 옮긴다.
    • 이를 통해 Profile 화면 클릭이 아닌 앱 접속시(MainTabController 실행시)에 사용자 정보를 firebase에서 가져오고 해당 사용자 정보를 이용해 user 객체 생성 ⇒ 탭바에 소속되는 다른 ViewController들을 생성할 때 해당 user 객체를 넘겨 주는 구조로 변경
  2. ProfileController.swift 에 init(user: User) 함수를 생성하여 객체 생성시에 user 정보(객체)를 받아오게 한다.
  3. AuthenticationDelegate 프로토콜을 LoginController.swift에 생성하여 사용자가 다른 계정으로 로그인하거나 새롭게 회원가입을 하였을 때 로그인 or 회원가입 controller를 단순히 dismiss 시키는 것이 아니라 새 사용자의 정보를 fetchUser() 하도록 변경한다.
  4. RegistrationController.swift 또한 LoginControleller와 마찬가지로 같은 목적을 위해 delegate를 생성한다.

MainTabController.swift

class MainTabController: UITabBarController {
    
    //MARK: - LifeCycle
    
    private var user: User? {
        didSet {
            guard let user = user else { return }
            configureViewControllers(withUser: user)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        checkIfUserIsLoggedIn()
        fetchUser()
    }
    
    //MARK: - API
    
    func fetchUser() {
        UserService.fetchUser { user in
            self.user = user
        }
    }

		func checkIfUserIsLoggedIn() {
        if Auth.auth().currentUser == nil {
            DispatchQueue.main.async {
                let controller = LoginController()
                controller.delegate = self
                let nav = UINavigationController(rootViewController: controller)
                nav.modalPresentationStyle = .fullScreen
                self.present(nav, animated: true, completion: nil)
            }
        }
    }
	... 중략 ...

		//MARK: - Helpers
    
    func configureViewControllers(withUser user: User) {
        view.backgroundColor = .white
        
        let layout = UICollectionViewFlowLayout()
        
        let feed = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "home_unselected"), selectedImage: #imageLiteral(resourceName: "home_selected"), rootViewController: FeedController(collectionViewLayout: layout))
        
        let search = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "search_unselected"), selectedImage: #imageLiteral(resourceName: "search_selected"), rootViewController: SearchController())
        
        let imageSelector = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "plus_unselected"), selectedImage: #imageLiteral(resourceName: "plus_unselected"), rootViewController: ImageSelectorController())
        
        let notifications = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "like_unselected"), selectedImage: #imageLiteral(resourceName: "like_selected"), rootViewController: NotificationController())
        
        let profileController = ProfileController(user: user)
        let profile = templateNavigationController(unselectedImage: #imageLiteral(resourceName: "profile_unselected"), selectedImage: #imageLiteral(resourceName: "profile_selected"), rootViewController: profileController)
        
        viewControllers = [feed, search, imageSelector, notifications, profile]
        
        tabBar.tintColor = .black
    }
}

//MARK: - AuthenticationDelegate

extension MainTabController: AuthenticationDelegate {
    func authenticationDidComplete() {
        fetchUser()
        self.dismiss(animated: true, completion: nil)
    }
}

ProfileController.swift

class ProfileController: UICollectionViewController {
    
    //MARK: - Properties
    
    private var user: User
    
    
    //MARK: - Lifecycle
    
    init(user: User) {
        self.user = user
        super.init(collectionViewLayout: UICollectionViewFlowLayout())
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

	... 중략 ...
}

LoginController.swift

protocol AuthenticationDelegate: AnyObject {
    func authenticationDidComplete()
}

class LoginController: UIViewController {
    
    //MARK: - Properties
    
    private var viewModel = LoginViewModel()
    
    weak var delegate: AuthenticationDelegate?

	... 중략 ...

		//MARK: - Actions
    
    @objc func handleLogin() {
        guard let email = emailTextField.text else { return }
        guard let password = passwordTextField.text else { return }
        
        AuthService.logUserIn(withEmail: email, password: password) { result, error in
            if let error = error {
                print("DEBUG: Failed to log user in \(error.localizedDescription)")
                return
            }

            // 로그인 화면 dismiss하고 로그인한 계정 정보 fetchUser() 시키기
            self.delegate?.authenticationDidComplete()
        }
    }

		@objc func handleShowSignUp(){
		    let controller = RegistrationController()
				// RegistrationController 에서도 회원가입이 완료되면 dismiss하고 회원가입한 계정 정보 fetchUser() 하기 위해 delegate 설정
		    controller.delegate = delegate
		    navigationController?.pushViewController(controller, animated: true)
	  }
	... 중략 ...
}

RegistrationController.swift

class RegistrationController: UIViewController {
    
    //MARK: - Properties
    
    private var viewModel = RegistrationViewModel()
    private var profileImage: UIImage?
    weak var delegate: AuthenticationDelegate?

	... 중략 ...

		//MARK: - Actions
    
    @objc func handleSignUp() {
        guard let email = emailTextField.text else { return }
        guard let password = passwordTextField.text else { return }
        guard let fullname = fullnameTextField.text else { return }
        guard let username = usernameTextField.text else { return }
        guard let profileImage = self.profileImage else { return }

        let credentials = AuthCredentials(email: email, password: password, fullname: fullname, username: username, profileImage: profileImage)
        AuthService.registerUser(withCredeitial: credentials) { error in
            if let error = error {
                print("DEBUG: Failed to register user \(error.localizedDescription)")
                return
            }

            // 회원가입 화면 dismiss하고 로그인한 계정 정보 fetchUser() 시키기
            self.delegate?.authenticationDidComplete()
        }
    }

	... 중략 ...
}

주의점

  • LoginController.swift와 RegistrationController.swift에서 delegate 변수를 선언 할 때 weak 로 선언하였다.
  • Why?
    • 두 컨트롤러에서의 delegate는 MainTabController.swift의 checkIfUserIsLoggedIn() 함수에서 self로 설정되게 된다. ⇒ delegate는 MainTabController 객체를 참조 ⇒ MainTabController는 SceneDelegate.swift에서도 이미 루트 뷰컨트롤러로 설정할 때 객체를 생성한 적이 있다. ⇒ 여러 객체끼리 참조를 하면서 프로그램이 꼬일 수 있다. (strong으로 변수 선언 시에) ⇒ 따라서 weak 참조로 변수를 선언한다. ⇒ 해당 인스턴스의 소유권이 아닌 주소값을 가지게 된다. (포인터 개념) ⇒ 참조하는 인스턴스 (여기서는 MainTabController)의 retain count 증가 x ⇒ avoid retain cycles
profile
나중은 결코 오지 않는다.

0개의 댓글