From 78e69a7ed6b480be23aed5f221e390a52429ca1a Mon Sep 17 00:00:00 2001 From: Sejin Lee Date: Mon, 17 Apr 2023 20:23:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[Fix]=20#170=20-=20=EC=86=9D=ED=8A=B8=20?= =?UTF-8?q?=ED=99=9C=EB=8F=99=20=EA=B0=9C=EC=9B=94=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Extension/Foundation+/Date+.swift | 28 ++++++++++ .../MainScene/ViewModel/MainViewModel.swift | 54 +++++++++---------- 2 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Date+.swift diff --git a/SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Date+.swift b/SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Date+.swift new file mode 100644 index 00000000..f1182685 --- /dev/null +++ b/SOPT-iOS/Projects/Core/Sources/Extension/Foundation+/Date+.swift @@ -0,0 +1,28 @@ +// +// Date+.swift +// Core +// +// Created by sejin on 2023/04/17. +// Copyright © 2023 SOPT-iOS. All rights reserved. +// + +import Foundation + +public extension Date { + /// Create a date from specified parameters + /// + /// - Parameters: + /// - year: The desired year + /// - month: The desired month + /// - day: The desired day + /// - Returns: A `Date` object + static func from(year: Int, month: Int, day: Int) -> Date? { + let calendar = Calendar(identifier: .gregorian) + var dateComponents = DateComponents() + dateComponents.timeZone = TimeZone.current + dateComponents.year = year + dateComponents.month = month + dateComponents.day = day + return calendar.date(from: dateComponents) ?? nil + } +} diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift index 71524ad9..f7e2b00d 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift @@ -74,7 +74,7 @@ extension MainViewModel { }.store(in: self.cancelBag) useCase.serviceState.asDriver() - .sink { [weak self] serviceState in + .sink { serviceState in output.isServiceAvailable.send(serviceState.isAvailable) }.store(in: self.cancelBag) } @@ -94,37 +94,31 @@ extension MainViewModel { } } + /// 최초 솝트 가입일로부터 몇달이 지났는지 계산 func calculateMonths() -> String? { - guard let userMainInfo = userMainInfo else { return nil } - if userMainInfo.status == "ACTIVE" && userMainInfo.historyList.count > 0 { - guard var currentMonth = getCurrentMonth() else { - return String(userMainInfo.historyList.count * 5) - } - - // 짝수 기수 -> 상반기 - if let recent = userMainInfo.historyList.first { - var currentGenerationTime = 0 - if recent % 2 == 0 { - // 기수 시작 3월 기준으로 계산 - currentGenerationTime = currentMonth - 3 + 1 - } else { - // 현재 1월 일 때 - if currentMonth < 3 { - currentMonth += 12 - } - currentGenerationTime = currentMonth - 9 + 1 - } - return String((userMainInfo.historyList.count-1)*5 + currentGenerationTime) - } - } - return String(userMainInfo.historyList.count * 5) + guard let userMainInfo = userMainInfo, let firstHistory = userMainInfo.historyList.last else { return nil } + guard let joinDate = calculateJoinDateWithFirstHistory(history: firstHistory), let monthDifference = calculateMonthDifference(since: joinDate) else { return nil } + + return String(monthDifference) } - private func getCurrentMonth() -> Int? { - let date = Date() - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "M" - let monthString = dateFormatter.string(from: date) - return Int(monthString) + // 파라미터로 넣은 기수의 시작 날짜를 리턴 + private func calculateJoinDateWithFirstHistory(history: Int) -> Date? { + let yearDifference = history / 2 + var month = (history % 2 == 0) ? 3 : 9 // 짝수 기수는 3월, 홀수 기수는 9월 시작 + // 1기를 2007년으로 계산 + return Date.from(year: yearDifference + 2007, month: month, day: 1) + } + + // 파라미터로 넣은 날짜로 부터 현재 몇달이 지났는지 계산 + private func calculateMonthDifference(since startDate: Date) -> Int? { + let calendar = Calendar.current + + let components = calendar.dateComponents([.month], from: startDate, to: .now) + guard let month = components.month else { + return nil + } + + return month >= 0 ? month + 1 : nil } } From c9d5b0fa5af3589f65c7e270ae0b0b130087a985 Mon Sep 17 00:00:00 2001 From: Sejin Lee Date: Mon, 17 Apr 2023 21:50:51 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[Chore]=20#170=20-=20let=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EC=84=A0=EC=96=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift index f7e2b00d..7e6b8693 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift @@ -105,7 +105,7 @@ extension MainViewModel { // 파라미터로 넣은 기수의 시작 날짜를 리턴 private func calculateJoinDateWithFirstHistory(history: Int) -> Date? { let yearDifference = history / 2 - var month = (history % 2 == 0) ? 3 : 9 // 짝수 기수는 3월, 홀수 기수는 9월 시작 + let month = (history % 2 == 0) ? 3 : 9 // 짝수 기수는 3월, 홀수 기수는 9월 시작 // 1기를 2007년으로 계산 return Date.from(year: yearDifference + 2007, month: month, day: 1) } From 6ffe481f869a15307364a93297c8e6fc62545f4d Mon Sep 17 00:00:00 2001 From: Sejin Lee Date: Mon, 17 Apr 2023 22:57:01 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[Feat]=20#170=20-=20=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=B7=B0=EC=97=90=EC=84=9C=20=EC=9C=A0=EC=A0=80=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8B=A4?= =?UTF-8?q?=EA=B0=80=20=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D=20=EC=8B=9C?= =?UTF-8?q?=20AlertView=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EA=B3=A0=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=EB=B2=84=ED=8A=BC=20=ED=81=B4=EB=A6=AD=ED=95=98?= =?UTF-8?q?=EB=A9=B4=20=EC=9E=AC=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Model/UserMainInfoModel.swift | 15 +++++--- .../Domain/Sources/UseCase/MainUseCase.swift | 1 + .../Sources/MainScene/VC/MainVC.swift | 34 +++++++++++++------ .../MainScene/ViewModel/MainViewModel.swift | 4 +-- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/SOPT-iOS/Projects/Domain/Sources/Model/UserMainInfoModel.swift b/SOPT-iOS/Projects/Domain/Sources/Model/UserMainInfoModel.swift index 9284c55b..cdae37cb 100644 --- a/SOPT-iOS/Projects/Domain/Sources/Model/UserMainInfoModel.swift +++ b/SOPT-iOS/Projects/Domain/Sources/Model/UserMainInfoModel.swift @@ -9,13 +9,15 @@ import Foundation public struct UserMainInfoModel { - public let status, name, profileImage: String + public let status, name: String + public let profileImage: String? public let historyList: [Int] - public let attendanceScore: Float - public let announcement: String + public let attendanceScore: Float? + public let announcement: String? public let responseMessage: String? + public var withError: Bool = false - public init(status: String, name: String, profileImage: String, historyList: [Int], attendanceScore: Float, announcement: String, responseMessage: String?) { + public init(status: String, name: String, profileImage: String?, historyList: [Int], attendanceScore: Float?, announcement: String?, responseMessage: String?) { self.status = status self.name = name self.profileImage = profileImage @@ -24,4 +26,9 @@ public struct UserMainInfoModel { self.announcement = announcement self.responseMessage = responseMessage } + + public init(withError: Bool) { + self.init(status: "", name: "", profileImage: nil, historyList: [], attendanceScore: nil, announcement: nil, responseMessage: nil) + self.withError = withError + } } diff --git a/SOPT-iOS/Projects/Domain/Sources/UseCase/MainUseCase.swift b/SOPT-iOS/Projects/Domain/Sources/UseCase/MainUseCase.swift index b76c36b4..dc61cbd7 100644 --- a/SOPT-iOS/Projects/Domain/Sources/UseCase/MainUseCase.swift +++ b/SOPT-iOS/Projects/Domain/Sources/UseCase/MainUseCase.swift @@ -33,6 +33,7 @@ public class DefaultMainUseCase { extension DefaultMainUseCase: MainUseCase { public func getUserMainInfo() { repository.getUserMainInfo() + .replaceError(with: UserMainInfoModel.init(withError: true)) .sink { event in print("MainUseCase: \(event)") } receiveValue: { [weak self] userMainInfoModel in diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/VC/MainVC.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/VC/MainVC.swift index c6aa8b08..c6d8c5de 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/VC/MainVC.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/VC/MainVC.swift @@ -17,6 +17,7 @@ import SnapKit import Then import AuthFeatureInterface +import BaseFeatureDependency import MainFeatureInterface import StampFeatureInterface import SettingFeatureInterface @@ -27,6 +28,7 @@ public class MainVC: UIViewController, MainViewControllable { & StampFeatureViewBuildable & SettingFeatureViewBuildable & AppMyPageFeatureViewBuildable + & AlertViewBuildable // MARK: - Properties @@ -34,8 +36,8 @@ public class MainVC: UIViewController, MainViewControllable { public var factory: factoryType! private var cancelBag = CancelBag() - private var userMainInfo: UserMainInfoModel? - + private var requestUserInfo = CurrentValueSubject(()) + // MARK: - UI Components private let naviBar = MainNavigationBar() @@ -93,16 +95,20 @@ extension MainVC { extension MainVC { private func bindViewModels() { - let input = MainViewModel.Input(viewDidLoad: Driver.just(())) + let input = MainViewModel.Input(requestUserInfo: self.requestUserInfo) let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag) output.getUserMainInfoDidComplete .sink { [weak self] _ in + guard let userMainInfo = self?.viewModel.userMainInfo, userMainInfo.withError == false else { + self?.presentNetworkAlertVC() + return + } self?.collectionView.reloadData() }.store(in: self.cancelBag) output.isServiceAvailable - .sink { [weak self] isServiceAvailable in + .sink { isServiceAvailable in print("현재 앱 서비스 사용 가능(심사 X)?: \(isServiceAvailable)") }.store(in: self.cancelBag) } @@ -114,12 +120,6 @@ extension MainVC { .sink { owner, _ in let viewController = owner.factory.makeAppMyPageVC(userType: owner.viewModel.userType).viewController owner.navigationController?.pushViewController(viewController, animated: true) - -// if owner.viewModel.userType == .visitor { -// owner.setRootViewToSignIn() -// return -// } -// owner.pushSettingFeature() }.store(in: self.cancelBag) } @@ -157,6 +157,20 @@ extension MainVC { let navigation = UINavigationController(rootViewController: factory.makeSignInVC().viewController) ViewControllerUtils.setRootViewController(window: self.view.window!, viewController: navigation, withAnimation: true) } + + private func presentNetworkAlertVC() { + let networkAlertVC = factory.makeAlertVC( + type: .titleDescription, + theme: .main, + title: I18N.Default.networkError, + description: I18N.Default.networkErrorDescription, + customButtonTitle: I18N.Default.ok, + customAction:{ [weak self] in + self?.requestUserInfo.send() + }).viewController + + self.present(networkAlertVC, animated: false) + } } // MARK: - UICollectionViewDelegate diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift index 7e6b8693..53513711 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/ViewModel/MainViewModel.swift @@ -28,7 +28,7 @@ public class MainViewModel: ViewModelType { // MARK: - Inputs public struct Input { - let viewDidLoad: Driver + let requestUserInfo: CurrentValueSubject } // MARK: - Outputs @@ -54,7 +54,7 @@ extension MainViewModel { let output = Output() self.bindOutput(output: output, cancelBag: cancelBag) - input.viewDidLoad + input.requestUserInfo .sink { [weak self] _ in guard let self = self else { return } if self.userType != .visitor {