diff --git a/mobile/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/mobile/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 19f8960f4..d6f6da0f7 100644 --- a/mobile/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/mobile/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ BF7B7CD72696AFE400278518 /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7B7CD62696AFE400278518 /* UIColor+Extension.swift */; }; BF7DE027267A418D0019B519 /* LoginViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF0A6C15266F5F8F000F3BC5 /* LoginViewController.storyboard */; }; BF87CBC12686FC40002F81FD /* URLRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8C7FBE2682242B005ABB38 /* URLRouter.swift */; }; + BF8AC85326AE7BE500D90C4C /* TokenAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8AC85226AE7BE500D90C4C /* TokenAction.swift */; }; + BF8AC85526AE7CAD00D90C4C /* AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8AC85426AE7CAD00D90C4C /* AppStore.swift */; }; BF8C7F052678905C005ABB38 /* ClientId.plist in Resources */ = {isa = PBXBuildFile; fileRef = BF8C7F042678905C005ABB38 /* ClientId.plist */; }; BF8C7F3A267A5E7D005ABB38 /* UIStoryBoard+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB399CD2671BB59002C6D80 /* UIStoryBoard+Extension.swift */; }; BF8C7F4D267A6131005ABB38 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BF8C7F4C267A6131005ABB38 /* KeychainSwift */; }; @@ -124,6 +126,8 @@ BF50FE65267FC3FC0084F73F /* UIAlertController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Extension.swift"; sourceTree = ""; }; BF50FE67268022950084F73F /* ASWebAuthPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASWebAuthPublisher.swift; sourceTree = ""; }; BF7B7CD62696AFE400278518 /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; + BF8AC85226AE7BE500D90C4C /* TokenAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenAction.swift; sourceTree = ""; }; + BF8AC85426AE7CAD00D90C4C /* AppStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStore.swift; sourceTree = ""; }; BF8C7F042678905C005ABB38 /* ClientId.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = ClientId.plist; sourceTree = ""; }; BF8C7F8A267B2C06005ABB38 /* LoginUITests2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginUITests2.swift; sourceTree = ""; }; BF8C7FBE2682242B005ABB38 /* URLRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRouter.swift; sourceTree = ""; }; @@ -352,6 +356,8 @@ isa = PBXGroup; children = ( BF9D0942269E204E009B6AA0 /* AppCoordinator.swift */, + BF8AC85426AE7CAD00D90C4C /* AppStore.swift */, + BF8AC85226AE7BE500D90C4C /* TokenAction.swift */, ); path = Coordinator; sourceTree = ""; @@ -592,6 +598,7 @@ BFB399CB2671B387002C6D80 /* Endpoint.swift in Sources */, BFA153E6269EE79000341A6D /* LoginViewCoordinator.swift in Sources */, BF9D08F9269CC905009B6AA0 /* UITabBarItem+Extension.swift in Sources */, + BF8AC85326AE7BE500D90C4C /* TokenAction.swift in Sources */, BFD75CFF26949C4E004695E9 /* LabelCell.swift in Sources */, BF251D252695A6AE00834E33 /* IssueLabel.swift in Sources */, BF0A6C12266F5F40000F3BC5 /* LoginViewModel.swift in Sources */, @@ -622,6 +629,7 @@ BF0E36A22695AE0B00835E49 /* IssueListCollectionDataSource.swift in Sources */, BF50FE66267FC3FC0084F73F /* UIAlertController+Extension.swift in Sources */, BFA153FB269F401900341A6D /* TabBarCoordinator.swift in Sources */, + BF8AC85526AE7CAD00D90C4C /* AppStore.swift in Sources */, BFB399CE2671BB59002C6D80 /* UIStoryBoard+Extension.swift in Sources */, BF0A6BD8266F4EE5000F3BC5 /* SceneDelegate.swift in Sources */, BFD2198E268EC02800FEF16C /* Issues.swift in Sources */, diff --git a/mobile/issue-tracker/issue-tracker/AppCycle/SceneDelegate.swift b/mobile/issue-tracker/issue-tracker/AppCycle/SceneDelegate.swift index 39d4f361a..05551ed92 100644 --- a/mobile/issue-tracker/issue-tracker/AppCycle/SceneDelegate.swift +++ b/mobile/issue-tracker/issue-tracker/AppCycle/SceneDelegate.swift @@ -26,10 +26,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.rootViewController = navigationController window?.makeKeyAndVisible() + let appStore = AppStore(state: .init()) appCoordinator = AppCoordinator(navigation: navigationController, dependency: .init(loginCoordinatorFactory: appDependency.makeLoginCoordinator, tabBarCoordinatorFactory: appDependency.makeTabBarCoordinator)) - + appCoordinator?.dispatch = appStore.dispatch(_:) + appStore.updateState = appCoordinator?.update(with:) appCoordinator?.loadInitalView() } } diff --git a/mobile/issue-tracker/issue-tracker/Coordinator/AppCoordinator.swift b/mobile/issue-tracker/issue-tracker/Coordinator/AppCoordinator.swift index 9c220f52b..621659f37 100644 --- a/mobile/issue-tracker/issue-tracker/Coordinator/AppCoordinator.swift +++ b/mobile/issue-tracker/issue-tracker/Coordinator/AppCoordinator.swift @@ -6,7 +6,6 @@ // import UIKit -import KeychainSwift final class AppCoordinator: Coordinator { @@ -17,9 +16,15 @@ final class AppCoordinator: Coordinator { var tabBarCoordinatorFactory: ((UINavigationController) -> TabBarCoordinator) } - private let loginCoordinator: LoginViewCoordinator + struct TokenState { + var token: TokenAction = .initial + } + + private var loginCoordinator: LoginViewCoordinator private let tabBarCoordinator: TabBarCoordinator + var dispatch: ((TokenAction) -> Void)? + init(navigation: UINavigationController = UINavigationController(), dependency: Dependency) { self.navigation = navigation @@ -29,32 +34,32 @@ final class AppCoordinator: Coordinator { } func loadInitalView() { - if isEmptyToken() { + dispatch?(.initial) + } + + func update(with state: TokenState) { + switch state.token { + case .empty: showLoginFlow() - } else { + case .existent, .completed: showTabBarFlow() + case .initial: + break } } private func showLoginFlow() { - loginCoordinator.delegate = self + loginCoordinator.authenticated = dispatch loginCoordinator.loadInitalView() } private func showTabBarFlow() { tabBarCoordinator.loadInitalView() } - - private func isEmptyToken() -> Bool { - guard let toggle = KeychainSwift().get("token")?.isEmpty else { - return true - } - return toggle - } } -extension AppCoordinator: LoginViewCoordinatorDelegate { - func completeLogin() { - showTabBarFlow() +extension AppCoordinator.TokenState { + init(state: AppStore.State) { + token = state.token } } diff --git a/mobile/issue-tracker/issue-tracker/Coordinator/AppStore.swift b/mobile/issue-tracker/issue-tracker/Coordinator/AppStore.swift new file mode 100644 index 000000000..60a87c52d --- /dev/null +++ b/mobile/issue-tracker/issue-tracker/Coordinator/AppStore.swift @@ -0,0 +1,50 @@ +// +// AppStore.swift +// issue-tracker +// +// Created by HOONHA CHOI on 2021/07/26. +// + +import Foundation +import Combine +import KeychainSwift + +final class AppStore { + + struct State { + var token: TokenAction = .initial + } + + struct Reducer { + func reduce(action: TokenAction, state: inout State) { + switch action { + case .initial: + state.token = (KeychainSwift().get("token")?.isEmpty ?? true) ? .empty : .existent + case .empty: + state.token = .empty + case .completed, .existent: + state.token = .existent + } + } + } + + private var reducer: Reducer { + return Reducer() + } + + @Published private(set) var state: State + private var cancellable: AnyCancellable? + + var updateState: ((AppCoordinator.TokenState) -> Void)? + + init(state: State) { + self.state = state + cancellable = $state.sink(receiveValue: { [weak self] state in + self?.updateState?(AppCoordinator.TokenState(token: state.token)) + }) + } + + func dispatch(_ action: TokenAction) { + reducer.reduce(action: action, state: &state) + } +} diff --git a/mobile/issue-tracker/issue-tracker/Coordinator/TokenAction.swift b/mobile/issue-tracker/issue-tracker/Coordinator/TokenAction.swift new file mode 100644 index 000000000..20cd486d9 --- /dev/null +++ b/mobile/issue-tracker/issue-tracker/Coordinator/TokenAction.swift @@ -0,0 +1,15 @@ +// +// AppAction.swift +// issue-tracker +// +// Created by HOONHA CHOI on 2021/07/26. +// + +import Foundation + +enum TokenAction { + case initial + case empty + case existent + case completed +} diff --git a/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewController.swift b/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewController.swift index d998e71f9..094e1d6e3 100644 --- a/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewController.swift +++ b/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewController.swift @@ -8,14 +8,10 @@ import UIKit import AuthenticationServices -protocol LoginViewControllerDelegate: AnyObject { - func didFinishLogin() -} - final class LoginViewController: UIViewController, StoryBoarded { var githubLoginHandler: ((ASWebAuthenticationPresentationContextProviding) -> Void)? - weak var delegate: LoginViewControllerDelegate? + var authorizeCompleteHandler: ((TokenAction) -> Void)? override func viewDidLoad() {} @@ -32,7 +28,7 @@ final class LoginViewController: UIViewController, StoryBoarded { func authorizeCompltion() { DispatchQueue.main.async { [weak self] in - self?.delegate?.didFinishLogin() + self?.authorizeCompleteHandler?(.completed) self?.dismiss(animated: true, completion: nil) } } diff --git a/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewCoordinator.swift b/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewCoordinator.swift index e088dab73..05355aee3 100644 --- a/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewCoordinator.swift +++ b/mobile/issue-tracker/issue-tracker/PresentationLayer/Login/LoginViewCoordinator.swift @@ -11,15 +11,11 @@ protocol LoginViewCoordinatorDependencies { func makeLoginViewController() -> LoginViewController } -protocol LoginViewCoordinatorDelegate: AnyObject { - func completeLogin() -} - -final class LoginViewCoordinator: Coordinator { +struct LoginViewCoordinator: Coordinator { var navigation: UINavigationController + var authenticated: ((TokenAction) -> Void)? private let loginViewControllerFactory: () -> LoginViewController - weak var delegate: LoginViewCoordinatorDelegate? init(navigation: UINavigationController, dependency: LoginViewCoordinatorDependencies) { @@ -29,13 +25,7 @@ final class LoginViewCoordinator: Coordinator { func loadInitalView() { let loginViewController = loginViewControllerFactory() - loginViewController.delegate = self + loginViewController.authorizeCompleteHandler = authenticated navigation.setViewControllers([loginViewController], animated: true) } } - -extension LoginViewCoordinator: LoginViewControllerDelegate { - func didFinishLogin() { - delegate?.completeLogin() - } -}