Onboarding - Part II

Seoyoung Lee·2022년 3월 1일
0

Professional iOS

목록 보기
6/12
post-thumbnail

Setting the stage

우리는 로그인 화면에서 로그인이 완료되면 Onboarding 화면으로 넘어가고, 또 onboarding 화면을 그만 보거나 다시 돌아가는 과정이 필요하다. 이 과정들을 구현하기 위해 protocol-delegate 패턴이 사용된다.

즉, protocol-delegate 패턴을 이용해서 로그인과 온보딩 과정에서 app delegate에게 신호를 보내는 것이다. 이 방법은 다른 뷰 컨트롤러에서도 적용될 수 있다. 그러나 우리는 유저가 한 번 온보딩 화면을 보면 그 다음부터는 온보딩 화면이 나타나지 않도록 해야 한다. 온보딩이 끝났는지와 끝난 시점을 감지하려면 뷰 컨트롤러들에게서 신호를 받을 수 있는 app delegate를 사용하는 것이 가장 좋은 방법일 것이다.

Protocol-delegate in action

LoginViewController 에서 LoginViewControllerDelegate 프로토콜을 작성하고, AppDelegate 에서 loginViewController 의 delegate를 self로 지정해준다.

💡 콘솔 창 깔끔하게 보는 팁 : print문에 특정한 키워드를 추가하고 (ex. `foo - Did Login`) 콘솔 창 우측 하단에 있는 Filter에서 내가 보고자 하는 키워드를 입력한다.

How to transition between view controller like a pro

로그인 화면에서 온보딩 화면으로 넘어가려면 rootViewController 를 온보딩 화면으로 설정하면 된다.

extension AppDelegate: LoginViewControllerDelegate {
    func didLogin() {
        window?.rootViewController = onboardingContainerViewController
    }
}

코드를 추가하고 앱을 실행해보면 문제없이 화면이 넘어가긴 하지만 애니메이션 없이 다른 화면이 띄워져서 딱딱하게 느껴진다.

extension AppDelegate {
    func setRootViewController(_ vc: UIViewController, animated: Bool = true) {
        guard animated, let window = self.window else {
            self.window?.rootViewController = vc
            self.window?.makeKeyAndVisible()
            return
        }
        
        window.rootViewController = vc
        window.makeKeyAndVisible()
        UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: nil, completion: nil)
    }
}

화면 이동을 할 때 애니메이션 효과를 넣기 위해 위와 같이 새로운 메소드를 추가해준다.

🤔 makeKeyAndVisible()? : https://ios-development.tistory.com/314 참고

Logging out

로그인 후 앱 내에서 로그아웃을 하면 다시 로그인 화면(초기 화면)으로 돌아가는 것을 구현하기 위해 임시로 DummyViewController 를 만들어준다. 이 뷰 컨트롤러에는 로그아웃 버튼이 있고, 온보딩 화면 다음에 보여질 예정이다. 로그아웃 역시 delegate를 만들어서 관련 메소드를 구현한다.

로그아웃 후 로그인 화면 상태 초기화하기

로그아웃을 하고 다시 로그인 화면으로 가면 로그인 성공 후의 상태(아이디, 비밀번호 텍스트필드 내 값, 버튼의 indicator)가 그대로 남아있다. 초기 모습으로 되돌리기 위해 viewDidDisappear(_:) 메소드에서 관련 작업을 해준다.

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    signInButton.configuration?.showsActivityIndicator = false
}

강의에서는 indicator 숨기는 코드만 추가했는데, 텍스트필드 값을 비워주는 작업도 해야 할 것 같다.

Onboarding once in memory

온보딩을 한 번 한 이후로 온보딩 화면이 뜨지 않게 하려면 어떻게 해야 할까?

먼저 메모리에 온보딩 상태를 저장하는 방법이 있다. AppDelegatehasOnboarded 라는 플래그 변수를 하나 선언하고 온보딩이 끝났을 때는 값을 true 로 지정한다. 로그인을 마쳤을 때는 hasOnboarded 의 상태에 따라 더미 뷰 컨트롤러 또는 온보딩 뷰 컨트롤러로 이동하도록 한다.

extension AppDelegate: LoginViewControllerDelegate {
    func didLogin() {
        if hasOnboarded {
            setRootViewController(dummyViewController)
        } else {
            setRootViewController(onboardingContainerViewController)
        }
    }
}

extension AppDelegate: OnboardingContainerViewControllerDelegate {
    func didFinishOnboarding() {
        hasOnboarded = true
        setRootViewController(dummyViewController)
    }
}

이 방법은 앱을 종료하거나 재시작하면 변수의 state가 초기화된다는 문제가 있다.

Onboarding once with UserDefaults

UserDefaults(NSUserDefaults)는 key와 value의 문자열 쌍을 저장할 수 있는 iOS 기기 내의 저장소이다. 기본적으로 사용자 설정이 저장되고, 앱을 실행할 때 이를 반영한다. UserDefaults에 저장된 데이터는 기기 내에 영구적으로 저장된다.

LocalState 클래스 만들기

import Foundation

public class LocalState {
    
    private enum Keys: String {
        case hasOnboarded
    }
    
    public static var hasOnboarded: Bool {
        get {
            return UserDefaults.standard.bool(forKey: Keys.hasOnboarded.rawValue)
        }
        set(newValue) {
            UserDefaults.standard.set(newValue, forKey: Keys.hasOnboarded.rawValue)
            UserDefaults.standard.synchronize()
        }
    }
}

UserDefaults에 저장할 데이터(온보딩 여부)를 관리하는 LocalState 클래스를 다음과 같이 만들어준다.

extension AppDelegate: LoginViewControllerDelegate {
    func didLogin() {
        if LocalState.hasOnboarded {
            setRootViewController(dummyViewController)
        } else {
            setRootViewController(onboardingContainerViewController)
        }
    }
}

extension AppDelegate: OnboardingContainerViewControllerDelegate {
    func didFinishOnboarding() {
        LocalState.hasOnboarded = true
        setRootViewController(dummyViewController)
    }
}

그리고 AppDelegate의 hasOnboarded 변수를 LocalState.hasOnboarded 로 수정한다.

profile
나의 내일은 파래 🐳

0개의 댓글

관련 채용 정보