diff --git a/Projects/App/Sources/Application/SceneDelegate.swift b/Projects/App/Sources/Application/SceneDelegate.swift index 633fb99e..fbc7ac16 100644 --- a/Projects/App/Sources/Application/SceneDelegate.swift +++ b/Projects/App/Sources/Application/SceneDelegate.swift @@ -25,10 +25,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let window = UIWindow(windowScene: windowScene) let userAuthToken = KeyChainManager.read(key: .authToken) let userNickName = UserInfoManager.userInfo?.userNickname - let organizationID = UserInfoManager.userInfo?.userOrganization + let isEmptyOrganization = UserInfoManager.userInfo?.userOrganization.isEmpty let navigationController = UINavigationController(rootViewController: ThirdPartyLoginViewController()) // 정상적으로 가입을 한경우 - if userAuthToken != nil && userNickName != nil && organizationID != nil { + if userAuthToken != nil && userNickName != nil && isEmptyOrganization == false { navigationController.pushViewController(PlaceListViewController(), animated: false) } window.rootViewController = navigationController diff --git a/Projects/App/Sources/Domain/User/UserInfoManager.swift b/Projects/App/Sources/Domain/User/UserInfoManager.swift index 0913fd3e..87c8c8d6 100644 --- a/Projects/App/Sources/Domain/User/UserInfoManager.swift +++ b/Projects/App/Sources/Domain/User/UserInfoManager.swift @@ -7,9 +7,18 @@ // import Foundation +import Combine final class UserInfoManager { + public static let shared = UserInfoManager() + + private var useCase = UserInfoManagerUseCase() + private var cancelBag = Set() + public var isNameFetched = PassthroughSubject() + + private init() {} + private enum UserInfoKeys: String, CaseIterable { case userNickname case userID @@ -100,4 +109,27 @@ final class UserInfoManager { UserInfoManager.userInfo?.userOrganization = userOrganization UserInfoManager.userInfo?.userOrgName = userOrgName } + +} + +extension UserInfoManager: ErrorMapper { + + // TODO: fetchOrganizationList 와 userOrgName 업데이트 분리 + func fetchOrganizationList(orgID: Int) { + useCase.fetchOrganizationList() + .sink { [weak self] error in + guard let self = self else { return } + switch error { + case .failure(let error): + let errorMessage = self.errorMessage(for: error) + print(errorMessage) + case .finished: break + } + } receiveValue: { [weak self] orgList in + UserInfoManager.userInfo?.userOrgName = orgList.first(where: {$0.id == orgID})?.name ?? "(인증대학없음)" + self?.isNameFetched.send(true) + } + .store(in: &cancelBag) + } + } diff --git a/Projects/App/Sources/Domain/User/UserInfoManagerUseCase.swift b/Projects/App/Sources/Domain/User/UserInfoManagerUseCase.swift new file mode 100644 index 00000000..3bce44dc --- /dev/null +++ b/Projects/App/Sources/Domain/User/UserInfoManagerUseCase.swift @@ -0,0 +1,23 @@ +// +// UserInfoManagerUseCase.swift +// App +// +// Created by Lee Myeonghwan on 2022/12/04. +// Copyright © 2022 com.zesty. All rights reserved. +// + +import Combine +import Foundation +import Network + +final class UserInfoManagerUseCase { + + func fetchOrganizationList() -> AnyPublisher<[Organization], NetworkError> { + return OrganizationAPI.fetchOrgList() + .map { orgList in + orgList.map { Organization(dto: $0) } + } + .eraseToAnyPublisher() + } + +} diff --git a/Projects/App/Sources/UI/Place/PlaceList/View/PlaceListViewController.swift b/Projects/App/Sources/UI/Place/PlaceList/View/PlaceListViewController.swift index ce865c7c..b05fb3b1 100644 --- a/Projects/App/Sources/UI/Place/PlaceList/View/PlaceListViewController.swift +++ b/Projects/App/Sources/UI/Place/PlaceList/View/PlaceListViewController.swift @@ -4,6 +4,7 @@ // // Created by 김태호 on 2022/10/25. // Updated by 리아 on 2022/11/01. +// Updated userOrg by 고반 on 2022/12/03. // Copyright (c) 2022 zesty. All rights reserved. // @@ -33,6 +34,8 @@ final class PlaceListViewController: UIViewController { private var emptyImageView = UIImageView() private var emptyLabel = UILabel() + private let placeTitle = UILabel() + // MARK: - LifeCycle init(viewModel: PlaceListViewModel = PlaceListViewModel()) { @@ -50,17 +53,48 @@ final class PlaceListViewController: UIViewController { configureDataSource() createLayout() configureUI() + bind() analytics() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - bind() + bindResult() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + // TODO: 계속 sink 가 쌓이면서 중복 실행되는 버그 해결을 위해 임시로 cancelBag 초기화 + cancelBag.removeAll() } // MARK: - Function + private func analytics() { + FirebaseAnalytics.Analytics.logEvent("place_list_viewed", parameters: [ + AnalyticsParameterScreenName: "place_list" + ]) + } + +} + +// MARK: - Bind Function + +extension PlaceListViewController { + private func bind() { + UserInfoManager.shared.isNameFetched + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self = self else { return } + // TODO: 계속 sink 가 쌓이면서 중복 실행되는 버그가 있음. cancelBag에서 binding이 제대로 해제가 안되는 것 같음. + self.placeTitle.text = UserInfoManager.userInfo?.userOrgName + self.navigationItem.leftBarButtonItem = UIBarButtonItem.init(customView: self.placeTitle) + } + .store(in: &cancelBag) + } + + private func bindResult() { viewModel.$result .receive(on: DispatchQueue.main) .sink { [weak self] placeList in @@ -71,12 +105,6 @@ final class PlaceListViewController: UIViewController { .store(in: &cancelBag) } - private func analytics() { - FirebaseAnalytics.Analytics.logEvent("place_list_viewed", parameters: [ - AnalyticsParameterScreenName: "place_list" - ]) - } - } extension PlaceListViewController: UICollectionViewDataSourcePrefetching, UICollectionViewDelegate { @@ -251,9 +279,14 @@ extension PlaceListViewController { let searchItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchButtonTapped)) let personCropCircle = UIImage(systemName: "person.crop.circle") let userInfoItem = UIBarButtonItem(image: personCropCircle, style: .plain, target: self, action: #selector(userInfoButtonTapped)) - let placeTitle = UILabel() let tapGesture = UITapGestureRecognizer(target: self, action: #selector(orgDetailButtonTapped)) - + + // TODO: 여러 조직을 가입을 대비하여 확장성 있게 구조 개선 + if UserInfoManager.userInfo?.userOrgName != nil { + placeTitle.text = UserInfoManager.userInfo?.userOrgName + } else if let orgID = UserInfoManager.userInfo?.userOrganization.first { + UserInfoManager.shared.fetchOrganizationList(orgID: orgID) + } placeTitle.text = UserInfoManager.userInfo?.userOrgName ?? "(인증대학없음)" placeTitle.font = .systemFont(ofSize: 17, weight: .bold) placeTitle.isUserInteractionEnabled = true diff --git a/Projects/App/Sources/UI/User/Login/ViewModel/ThirdPartyLoginViewModel.swift b/Projects/App/Sources/UI/User/Login/ViewModel/ThirdPartyLoginViewModel.swift index 17de9cf8..adf2aa93 100644 --- a/Projects/App/Sources/UI/User/Login/ViewModel/ThirdPartyLoginViewModel.swift +++ b/Projects/App/Sources/UI/User/Login/ViewModel/ThirdPartyLoginViewModel.swift @@ -83,7 +83,8 @@ final class ThirdPartyLoginViewModel { self.shouldSetNicknameSubject.send(true) return } - if UserInfoManager.userInfo?.userOrganization == nil { + let isEmptyOrganization = UserInfoManager.userInfo?.userOrganization.isEmpty ?? true + if isEmptyOrganization { self.shouldSetOrganizationSubject.send(true) return }