From 451f9aa989ee19e986ebf11fc9cfbc7c65f3e4b8 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Mon, 11 Mar 2024 14:17:54 +0900 Subject: [PATCH 01/21] =?UTF-8?q?[Fix]=20#241=20-=20Calendar=20UI=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 --- .../DetailAchievementCollectionViewCell.swift | 16 +- .../AchievementViewController.swift | 16 +- .../Cells/DateCollectionViewCell.swift | 5 +- .../Common/Calendar/CalendarView.swift | 165 ++++++++++-------- .../Common/Calendar/WeekMonthFSCalendar.swift | 41 +++-- .../DetailCalendarViewController.swift | 2 +- .../ViewControllers/HomeViewController.swift | 10 +- 7 files changed, 135 insertions(+), 120 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift index c665491c..eb2feeda 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift @@ -18,8 +18,8 @@ final class DetailAchievementCollectionViewCell: UICollectionViewCell { // MARK: - UI Components - let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) - let titleLabel = UILabel() + private let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) + private let titleLabel = UILabel() private let checkImage = UIImageView() // MARK: - Life Cycle @@ -50,7 +50,7 @@ extension DetailAchievementCollectionViewCell { $0.textColor = .gray1 $0.layer.cornerRadius = 25/2 } - + titleLabel.do { $0.font = .Pretendard(.semiBold, size: 16) $0.textColor = .gray2 @@ -65,12 +65,12 @@ extension DetailAchievementCollectionViewCell { private func setLayout() { contentView.addSubviews(tagLabel, titleLabel, checkImage) - + tagLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(22) $0.leading.equalToSuperview().inset(28) } - + titleLabel.snp.makeConstraints { $0.top.equalTo(tagLabel.snp.bottom).offset(7) $0.leading.equalToSuperview().inset(28) @@ -89,10 +89,6 @@ extension DetailAchievementCollectionViewCell { tagLabel.text = model.situationName titleLabel.text = model.title titleLabel.lineBreakMode = .byTruncatingTail - - switch model.completionStatus { - case .CHECKED: checkImage.isHidden = false - case .UNCHECKED: checkImage.isHidden = true - } + checkImage.isHidden = model.completionStatus == .UNCHECKED } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 28b52b0c..55441837 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -24,7 +24,7 @@ final class AchievementViewController: UIViewController { private let scrollView = UIScrollView() private let achievementLabel = UILabel() - private let monthCalendar = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + private let monthCalendar = CalendarView(scope: .month) private let statisticsView = StatisticsView() // MARK: - Life Cycle @@ -33,9 +33,9 @@ final class AchievementViewController: UIViewController { super.viewWillAppear(animated) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) - if let today = monthCalendar.calendar.today { + if let today = monthCalendar.today() { monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) - monthCalendar.calendar.currentPage = today + monthCalendar.currentPage(date: today) requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: today)) } } @@ -70,13 +70,7 @@ extension AchievementViewController { $0.layer.cornerRadius = 12 $0.layer.borderWidth = 1 $0.layer.borderColor = UIColor.gray4?.cgColor - $0.calendar.register(MissionCalendarCell.self, forCellReuseIdentifier: MissionCalendarCell.identifier) - $0.calendar.delegate = self - $0.calendar.dataSource = self - $0.monthCalendarClosure = { [weak self] month in - guard let self else { return } - self.requestMonthAPI(month: month) - } + $0.configure(delegate: self, datasource: self) } } @@ -115,7 +109,7 @@ extension AchievementViewController { let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) - self.monthCalendar.calendar.collectionView.reloadData() + self.monthCalendar.reloadCollectionView() } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift index 213e292c..09ad886b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift @@ -28,7 +28,7 @@ final class DateCollectionViewCell: UICollectionViewCell, AddMissionMenu { private let titleLabel = TitleLabel(title: I18N.date) private let subTitleLabel = SubTitleLabel(subTitle: I18N.subDateTitle, colorText: nil) - let calendarView = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + let calendarView = CalendarView(scope: .month) private let warningLabel = UILabel() private let stackView = UIStackView() @@ -121,9 +121,8 @@ extension DateCollectionViewCell { } calendarView.do { - $0.calendar.backgroundColor = .clear $0.backgroundColor = .clear - $0.calendar.delegate = self + $0.configure(delegate: self, datasource: nil) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 5958ecfb..bf1c35e8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -11,35 +11,52 @@ import FSCalendar import Then import SnapKit -protocol CalendarViewDelegate: AnyObject { +protocol WeekCalendarDelegate: AnyObject { func todayBtnTapped() } +protocol MonthCalendarDelegate: AnyObject { + + func changeMonth(with month: String) +} final class CalendarView: UIView { // MARK: - Properties - - let today = Date() - var monthCalendarClosure: ((_ month: String) -> Void)? - weak var delegate: CalendarViewDelegate? + + private enum CalendarMoveType { + case previous + case next + + var monthOffset: Int { + switch self { + case .previous: + return -1 + case .next: + return 1 + } + } + } + + weak var delegate: WeekCalendarDelegate? // MARK: - UI Components private let yearMonthLabel = UILabel() - let todayButton = UIButton(configuration: .filled()) + private let todayButton = UIButton(configuration: .filled()) private let horizonStackView = UIStackView() private let leftButton = UIButton() private let rightButton = UIButton() - var calendar = WeekMonthFSCalendar() + let calendar: WeekMonthFSCalendar // MARK: - Life Cycle - init(calendarScope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { + init(scope: FSCalendarScope) { + self.calendar = WeekMonthFSCalendar(calendarScope: scope) super.init(frame: .zero) - setCalendar(scope: calendarScope, scrollDirection: scrollDirection) + setUI() - setLayout(scope: calendarScope) + setLayout(scope: scope) } required init?(coder: NSCoder) { @@ -51,11 +68,6 @@ final class CalendarView: UIView { extension CalendarView { - private func setCalendar(scope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { - - calendar = WeekMonthFSCalendar(calendarScope: scope, scrollDirection: scrollDirection) - } - private func setUI() { backgroundColor = .ntdBlack @@ -106,62 +118,69 @@ extension CalendarView { } private func setLayout(scope: FSCalendarScope) { - switch scope { case .week: - addSubviews(calendar, yearMonthLabel, todayButton) - - yearMonthLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(23) - $0.leading.equalToSuperview().offset(20) - } - - todayButton.snp.makeConstraints { - $0.top.equalTo(yearMonthLabel.snp.top) - $0.trailing.equalToSuperview().inset(18) - $0.size.equalTo(CGSize(width: 60, height: 30)) - } - - calendar.snp.makeConstraints { - $0.top.equalTo(yearMonthLabel.snp.bottom).offset(8) - $0.directionalHorizontalEdges.equalToSuperview().inset(11) - $0.bottom.equalToSuperview().inset(20) - } - + setWeekLayout() case .month: - addSubviews(horizonStackView, calendar) - horizonStackView.addArrangedSubviews(leftButton, yearMonthLabel, rightButton) - - [leftButton, rightButton].forEach { - $0.snp.makeConstraints { - $0.size.equalTo(CGSize(width: 25, height: 25)) - } - } - - horizonStackView.snp.makeConstraints { - $0.top.equalToSuperview().offset(25) - $0.centerX.equalToSuperview() - $0.height.equalTo(25) - } - - calendar.snp.makeConstraints { - $0.top.equalTo(horizonStackView.snp.bottom).offset(20) - $0.directionalHorizontalEdges.equalToSuperview().inset(15) - $0.bottom.equalToSuperview().inset(25) - } + setMonthLayout() @unknown default: return } } - func scrollCurrentPage(calendar: WeekMonthFSCalendar, isPrev: Bool) { + private func setWeekLayout() { + addSubviews(calendar, yearMonthLabel, todayButton) - let gregorian = Calendar(identifier: .gregorian) - calendar.setCurrentPage( gregorian.date(byAdding: calendar.scope == .week ? .weekOfMonth : .month, value: isPrev ? -1 : 1, to: calendar.currentPage)!, animated: true) - let monthDateFormatter = DateFormatter() - monthDateFormatter.dateFormat = "yyyy-MM" - let stringDate = monthDateFormatter.string(from: calendar.currentPage) - monthCalendarClosure?(stringDate) + yearMonthLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(23) + $0.leading.equalToSuperview().offset(20) + } + + todayButton.snp.makeConstraints { + $0.top.equalTo(yearMonthLabel.snp.top) + $0.trailing.equalToSuperview().inset(18) + $0.size.equalTo(CGSize(width: 60, height: 30)) + } + + calendar.snp.makeConstraints { + $0.top.equalTo(yearMonthLabel.snp.bottom).offset(8) + $0.directionalHorizontalEdges.equalToSuperview().inset(11) + $0.bottom.equalToSuperview().inset(20) + } + } + + private func setMonthLayout() { + addSubviews(horizonStackView, calendar) + horizonStackView.addArrangedSubviews(leftButton, yearMonthLabel, rightButton) + + [leftButton, rightButton].forEach { + $0.snp.makeConstraints { + $0.size.equalTo(CGSize(width: 25, height: 25)) + } + } + + horizonStackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(25) + $0.centerX.equalToSuperview() + $0.height.equalTo(25) + } + + calendar.snp.makeConstraints { + $0.top.equalTo(horizonStackView.snp.bottom).offset(20) + $0.directionalHorizontalEdges.equalToSuperview().inset(15) + $0.bottom.equalToSuperview().inset(25) + } + } + + private func changeMonth(with type: CalendarMoveType) { + let calendar = Calendar.current + let currentPage = self.calendar.currentPage + var dateComponents = DateComponents() + dateComponents.month = type.monthOffset + + guard let changedCurrentPage = calendar.date(byAdding: dateComponents, to: currentPage) else { return } + + self.calendar.setCurrentPage(changedCurrentPage, animated: true) } } @@ -171,22 +190,18 @@ extension CalendarView { @objc func prevBtnTapped(_sender: UIButton) { - scrollCurrentPage(calendar: calendar, isPrev: true) + changeMonth(with: .previous) } @objc func nextBtnTapped(_sender: UIButton) { - scrollCurrentPage(calendar: calendar, isPrev: false) + changeMonth(with: .next) } -} - -extension CalendarView { @objc func todayBtnTapped(_sender: UIButton) { delegate?.todayBtnTapped() } - } extension CalendarView { @@ -197,7 +212,7 @@ extension CalendarView { } } - func configure(delegate: FSCalendarDelegate, datasource: FSCalendarDataSource) { + func configure(delegate: FSCalendarDelegate?, datasource: FSCalendarDataSource?) { calendar.delegate = delegate calendar.dataSource = datasource } @@ -218,4 +233,16 @@ extension CalendarView { $0.bottom.equalToSuperview().inset(45) } } + + func today() -> Date? { + return calendar.today + } + + func currentPage(date: Date) { + calendar.currentPage = date + } + + func select(date: Date?) { + calendar.select(date) + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift index 9dd31f33..e5db9f24 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift @@ -13,16 +13,13 @@ final class WeekMonthFSCalendar: FSCalendar { // MARK: - Life Cycle - override init(frame: CGRect) { - super.init(frame: frame) - } - - init(calendarScope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { + init(calendarScope: FSCalendarScope) { super.init(frame: .zero) self.scope = calendarScope - self.scrollDirection = scrollDirection + + setUI() configure(scope: calendarScope) - weekdayTitleStyle(scope: calendarScope) + weekdayTitleStyle() } required init?(coder: NSCoder) { @@ -33,15 +30,18 @@ final class WeekMonthFSCalendar: FSCalendar { // MARK: - Methods extension WeekMonthFSCalendar { - func configure(scope: FSCalendarScope) { + + func setUI() { + locale = Locale(identifier: "ko_KR") calendarHeaderView.isHidden = true headerHeight = 0 backgroundColor = .clear + scrollDirection = .horizontal appearance.titleDefaultColor = .white appearance.titleFont = .Pretendard(.medium, size: 14) - + appearance.subtitleSelectionColor = .white appearance.subtitleDefaultColor = .white appearance.subtitleFont = .Pretendard(.medium, size: 14) @@ -49,7 +49,10 @@ extension WeekMonthFSCalendar { appearance.todayColor = .clear appearance.selectionColor = .clear appearance.borderDefaultColor = .clear - + } + + func configure(scope: FSCalendarScope) { + switch scope { case .week: calendarWeekdayView.removeFromSuperview() @@ -67,17 +70,13 @@ extension WeekMonthFSCalendar { } } - func weekdayTitleStyle(scope: FSCalendarScope) { - switch self.scope { - case .week: - return - case .month: - I18N.weekDay.forEach { - calendarWeekdayView.weekdayLabels[Int($0) ?? 0].text = $0 - } - appearance.headerMinimumDissolvedAlpha = 0 - @unknown default: - return + func weekdayTitleStyle() { + guard self.scope == .month else { return } + + I18N.weekDay.enumerated().forEach { index, day in + calendarWeekdayView.weekdayLabels[index].text = day } + appearance.headerMinimumDissolvedAlpha = 0 + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 42921fef..0b03d5f1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -32,7 +32,7 @@ final class DetailCalendarViewController: UIViewController { // MARK: - UI Components - private let monthCalendar = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + private let monthCalendar = CalendarView(scope: .month) private let completeButton = UIButton() private let subLabel = UILabel() diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 13df4837..4f6fb65a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -31,7 +31,7 @@ final class HomeViewController: UIViewController { private var missionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) private lazy var missionDataSource = HomeDataSource(collectionView: missionCollectionView, missionList: missionList) - private let weekCalendar = CalendarView(calendarScope: .week, scrollDirection: .horizontal) + private let weekCalendar = CalendarView(scope: .week) private let addButton = UIButton() // MARK: - Life Cycle @@ -87,7 +87,7 @@ extension HomeViewController { private func setLayout() { view.addSubviews(weekCalendar, missionCollectionView, addButton) - weekCalendar.calendar.select(today) + weekCalendar.select(date: today) weekCalendar.snp.makeConstraints { $0.top.equalTo(safeArea) @@ -111,7 +111,7 @@ extension HomeViewController { // MARK: - Action -extension HomeViewController: CalendarViewDelegate { +extension HomeViewController: WeekCalendarDelegate { @objc func addBtnTapped(_sender: UIButton) { @@ -126,7 +126,7 @@ extension HomeViewController: CalendarViewDelegate { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickReturnToday) - weekCalendar.calendar.select(today) + weekCalendar.select(date: today) weekCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: today)) } @@ -190,7 +190,7 @@ extension HomeViewController: UICollectionViewDelegate { guard let self else { return } let modifiedDate: Date = date.toDate(withFormat: "YYYY.MM.dd") - self.weekCalendar.calendar.select(modifiedDate) + self.weekCalendar.select(date: modifiedDate) self.requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: modifiedDate)) } From 504ae92905d87d5a0889eb3fbd138c1e08f8137b Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Mon, 11 Mar 2024 14:18:07 +0900 Subject: [PATCH 02/21] =?UTF-8?q?[Move]=20#241=20-=20View=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ViewControllers => Views}/DetailAchieveHeaderView.swift | 0 .../Achievement/{ViewControllers => Views}/StatisticsView.swift | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/{ViewControllers => Views}/DetailAchieveHeaderView.swift (100%) rename iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/{ViewControllers => Views}/StatisticsView.swift (100%) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift similarity index 100% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/StatisticsView.swift similarity index 100% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/StatisticsView.swift From d619b7a4d31847d4f3651cd82d58ad3898f25da7 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 12:50:56 +0900 Subject: [PATCH 03/21] =?UTF-8?q?[Fix]=20#241=20-=20=EC=A4=91=EB=B3=B5=20r?= =?UTF-8?q?esponse=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 53 +++++++++++++------ .../Network/API/Mission/MissionAPI.swift | 15 +++--- ...onseDTO.swift => CalendarReponseDTO.swift} | 2 +- .../Home/WeekMissionResponseDTO.swift | 31 ----------- .../AchievementViewController.swift | 23 ++++---- 5 files changed, 57 insertions(+), 67 deletions(-) rename iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/{AchieveCalendarResponseDTO.swift => CalendarReponseDTO.swift} (94%) delete mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index a4f643fd..a7b50210 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -14,10 +14,9 @@ 092C09B52A484DD900E9B06B /* HomeDeleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092C09B42A484DD900E9B06B /* HomeDeleteViewController.swift */; }; 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092C09B62A48596500E9B06B /* DeleteModalView.swift */; }; 092E04B129BD9C86008A5892 /* MissionDetailCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092E04B029BD9C86008A5892 /* MissionDetailCollectionViewCell.swift */; }; - 0930D37329B4FCAE0000C4AE /* StatisticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */; }; 0930DE6229B80550007958DE /* MissionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0930DE6129B80550007958DE /* MissionDetailViewController.swift */; }; 093DB0372A146BF900ECA5F6 /* MyInfoURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB0362A146BF900ECA5F6 /* MyInfoURL.swift */; }; - 093DB03D2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */; }; + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */; }; 093DB03F2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */; }; 0943A9F52A531D0000614761 /* Amplitude in Frameworks */ = {isa = PBXBuildFile; productRef = 0943A9F42A531D0000614761 /* Amplitude */; }; 0943A9F92A53239200614761 /* Bundle+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0943A9F82A53239200614761 /* Bundle+.swift */; }; @@ -26,6 +25,10 @@ 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */; }; 09582B4F29BEBAFA00EF3207 /* DetailCalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */; }; 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */; }; + 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */; }; + 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */; }; + 095FEE162B9ED28800FF44C0 /* DetailAchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */; }; + 095FEE182B9ED30A00FF44C0 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */; }; 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; }; 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */; }; 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */; }; @@ -42,12 +45,10 @@ 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98029B3094F005B37E6 /* WeekMonthFSCalendar.swift */; }; 099FC98329B30A2E005B37E6 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98229B30A2E005B37E6 /* Utils.swift */; }; 099FC98929B3233D005B37E6 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98829B3233D005B37E6 /* CalendarView.swift */; }; - 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */; }; 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */; }; 09A8E48C2B9DBE5C00C0F48F /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 09A8E48B2B9DBE5C00C0F48F /* API_KEY.plist */; }; 09A8E48E2B9DBEC700C0F48F /* BaseAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */; }; 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; - 09CF56022B09E98A00526C8C /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */; }; 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56032B09F23800526C8C /* HomeDataSource.swift */; }; 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; }; 09DCCD232A18EFB0003DCF8A /* MissionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD222A18EFB0003DCF8A /* MissionAPI.swift */; }; @@ -179,10 +180,9 @@ 092C09B42A484DD900E9B06B /* HomeDeleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDeleteViewController.swift; sourceTree = ""; }; 092C09B62A48596500E9B06B /* DeleteModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteModalView.swift; sourceTree = ""; }; 092E04B029BD9C86008A5892 /* MissionDetailCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailCollectionViewCell.swift; sourceTree = ""; }; - 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsView.swift; sourceTree = ""; }; 0930DE6129B80550007958DE /* MissionDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailViewController.swift; sourceTree = ""; }; 093DB0362A146BF900ECA5F6 /* MyInfoURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoURL.swift; sourceTree = ""; }; - 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveCalendarResponseDTO.swift; sourceTree = ""; }; + 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReponseDTO.swift; sourceTree = ""; }; 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailResponseDTO.swift; sourceTree = ""; }; 0943A9F82A53239200614761 /* Bundle+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+.swift"; sourceTree = ""; }; 09582B4729BDA7F600EF3207 /* DetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailStackView.swift; sourceTree = ""; }; @@ -190,6 +190,10 @@ 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderView.swift; sourceTree = ""; }; 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCalendarViewController.swift; sourceTree = ""; }; 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewController.swift; sourceTree = ""; }; + 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = ""; }; + 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsView.swift; sourceTree = ""; }; + 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModel.swift; sourceTree = ""; }; + 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtil.swift; sourceTree = ""; }; 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultKeys.swift; sourceTree = ""; }; 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = ""; }; @@ -206,11 +210,9 @@ 099FC98029B3094F005B37E6 /* WeekMonthFSCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekMonthFSCalendar.swift; sourceTree = ""; }; 099FC98229B30A2E005B37E6 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 099FC98829B3233D005B37E6 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; - 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekMissionResponseDTO.swift; sourceTree = ""; }; 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAnotherDayResponseDTO.swift; sourceTree = ""; }; 09A8E48B2B9DBE5C00C0F48F /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = API_KEY.plist; path = ../../../../Desktop/API_KEY.plist; sourceTree = ""; }; 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAPI.swift; sourceTree = ""; }; - 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = ""; }; 09CF56032B09F23800526C8C /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = ""; }; 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyMissionResponseDTO.swift; sourceTree = ""; }; 09DCCD222A18EFB0003DCF8A /* MissionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionAPI.swift; sourceTree = ""; }; @@ -382,6 +384,7 @@ children = ( 0921611A2A5727EF0019CC8C /* AnalyticsEventProtocol.swift */, 0921611C2A57D0920019CC8C /* AmplitudeAnalyticsService.swift */, + 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */, ); path = Protocol; sourceTree = ""; @@ -430,6 +433,24 @@ path = Enum; sourceTree = ""; }; + 095FEE0F2B9ED15600FF44C0 /* Views */ = { + isa = PBXGroup; + children = ( + 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */, + 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */, + ); + name = Views; + path = "iOS-NOTTODO/Presentation/Achievement/Views"; + sourceTree = SOURCE_ROOT; + }; + 095FEE142B9ED27F00FF44C0 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 0987C83E2B9DD234007EE8DE /* MissionService */ = { isa = PBXGroup; children = ( @@ -491,7 +512,6 @@ 09A363CE2A45F06C003F8DB5 /* Home */ = { isa = PBXGroup; children = ( - 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */, 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */, 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */, 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */, @@ -502,7 +522,7 @@ 09A363CF2A45F074003F8DB5 /* Achieve */ = { isa = PBXGroup; children = ( - 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */, + 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */, ); path = Achieve; sourceTree = ""; @@ -572,8 +592,6 @@ children = ( 3B027AA1299C355800BEB65C /* AchievementViewController.swift */, 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */, - 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */, - 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */, ); path = ViewControllers; sourceTree = ""; @@ -758,6 +776,8 @@ 3B027A9B299C348800BEB65C /* Achievement */ = { isa = PBXGroup; children = ( + 095FEE142B9ED27F00FF44C0 /* ViewModel */, + 095FEE0F2B9ED15600FF44C0 /* Views */, 09F6719929CC8ECD00708725 /* ViewControllers */, 09F6719829CC8EC500708725 /* Cell */, ); @@ -1288,6 +1308,7 @@ 3B027A94299C340600BEB65C /* UIFont+.swift in Sources */, 6CA208292A191185001C4247 /* UIImageView+.swift in Sources */, 0982DE5A29AE5E6000D933D2 /* CompositionalLayout.swift in Sources */, + 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */, 3B482FA3299EA9CB00BCF424 /* TabBarItem.swift in Sources */, 3B027AA0299C353700BEB65C /* AddMissionViewController.swift in Sources */, 098BFD5929B7999E008E80F9 /* MyProfileCollectionViewCell.swift in Sources */, @@ -1335,7 +1356,6 @@ 155E456D2B62B1A1008628E7 /* UpdateCheckViewController.swift in Sources */, 3B14A13D29A6FBD300F92897 /* UIView+.swift in Sources */, 09F6719529CBFCD200708725 /* GradientView.swift in Sources */, - 09CF56022B09E98A00526C8C /* DetailAchieveHeaderView.swift in Sources */, 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */, 6CA208252A18FEEA001C4247 /* RecommendService.swift in Sources */, 3B482FA5299EAB8800BCF424 /* TabBarController.swift in Sources */, @@ -1344,6 +1364,7 @@ 09DCCD232A18EFB0003DCF8A /* MissionAPI.swift in Sources */, 09582B4829BDA7F600EF3207 /* DetailStackView.swift in Sources */, 09F6718429CADB1100708725 /* OnboardingModel.swift in Sources */, + 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */, 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */, 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */, 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */, @@ -1351,7 +1372,7 @@ 3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */, 6C16015829C40112005AE3F5 /* AuthButtonView.swift in Sources */, 3BC19A9329CA1CA800C02803 /* UICollectionViewCell+.swift in Sources */, - 093DB03D2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift in Sources */, + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */, 3B0CBA242A461B1C0004F2DB /* RecentMissionResponseDTO.swift in Sources */, 3B5F8F7F29BF900A0063A7F8 /* DateCollectionViewCell.swift in Sources */, 3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */, @@ -1362,8 +1383,6 @@ 6CD4F8BE29AA495900CCC740 /* RecommendActionCollectionViewCell.swift in Sources */, 6CF4707429A73D4C008D145C /* RecommendCollectionViewCell.swift in Sources */, 3BBB6C8F2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift in Sources */, - 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */, - 0930D37329B4FCAE0000C4AE /* StatisticsView.swift in Sources */, 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */, 6CF4706A29A71D71008D145C /* Strings.swift in Sources */, 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */, @@ -1374,6 +1393,7 @@ 0987C8402B9DD4DC007EE8DE /* MissionService.swift in Sources */, 6CD4F8C229AA5AF200CCC740 /* UIButton+.swift in Sources */, 6CA208362A1957CA001C4247 /* AuthAPI.swift in Sources */, + 095FEE162B9ED28800FF44C0 /* DetailAchievementViewModel.swift in Sources */, 6CF4706529A690E5008D145C /* NetworkBase.swift in Sources */, 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */, 6C16016429C5E37D005AE3F5 /* MyInfoAccountStackView.swift in Sources */, @@ -1396,6 +1416,7 @@ 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */, 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */, 155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */, + 095FEE182B9ED30A00FF44C0 /* ViewModel.swift in Sources */, 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */, 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */, 3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift index 7d200e8d..20d414c0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift @@ -10,9 +10,8 @@ import Foundation import Moya typealias DailyMissionData = GeneralArrayResponse -typealias WeekMissionData = GeneralArrayResponse typealias DetailMissionData = GeneralResponse -typealias AchieveCalendarData = GeneralArrayResponse +typealias CalendarData = GeneralArrayResponse typealias RecentMissionData = GeneralArrayResponse typealias UpdateMissionData = GeneralResponse typealias AddMissionData = GeneralResponse @@ -21,10 +20,10 @@ typealias UpdateMissionStatus = GeneralResponse protocol MissionAPIType { func getDailyMission(date: String, completion: @escaping (DailyMissionData?) -> Void) - func getWeeklyMissoin(startDate: String, completion: @escaping (WeekMissionData?) -> Void) + func getWeeklyMissoin(startDate: String, completion: @escaping (CalendarData?) -> Void) func getDetailMission(id: Int, completion: @escaping (DetailMissionData?) -> Void) func particularMissionDates(id: Int, completion: @escaping (GeneralArrayResponse) -> Void) - func getAchieveCalendar(month: String, completion: @escaping (AchieveCalendarData?) -> Void) + func getAchieveCalendar(month: String, completion: @escaping (CalendarData?) -> Void) func getRecentMission(completion: @escaping (RecentMissionData?) -> Void) func deleteMission(id: Int, completion: @escaping (GeneralResponse?) -> Void) func patchUpdateMissionStatus(id: Int, status: String, completion: @escaping (UpdateMissionStatus?) -> Void) @@ -58,12 +57,12 @@ final class MissionAPI: MissionAPIType { } } - func getWeeklyMissoin(startDate: String, completion: @escaping (WeekMissionData?) -> Void) { + func getWeeklyMissoin(startDate: String, completion: @escaping (CalendarData?) -> Void) { provider.request(.missionWeekly(startDate: startDate)) { result in switch result { case .success(let response): do { - let response = try response.map(WeekMissionData?.self) + let response = try response.map(CalendarData?.self) completion(response) } catch let err { print(err.localizedDescription, 500) @@ -93,12 +92,12 @@ final class MissionAPI: MissionAPIType { } } - func getAchieveCalendar(month: String, completion: @escaping (AchieveCalendarData?) -> Void) { + func getAchieveCalendar(month: String, completion: @escaping (CalendarData?) -> Void) { provider.request(.achieveCalendar(month: month)) { result in switch result { case .success(let response): do { - let response = try response.map(AchieveCalendarData?.self) + let response = try response.map(CalendarData?.self) completion(response) } catch let err { print(err.localizedDescription, 500) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift similarity index 94% rename from iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift rename to iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift index 333d7794..67b49b1b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift @@ -7,7 +7,7 @@ import Foundation -struct AchieveCalendarResponseDTO: Codable { +struct CalendarReponseDTO: Codable { let actionDate: String let percentage: Float diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift deleted file mode 100644 index 099e8f0a..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// WeekMissionResponseDTO.swift -// iOS-NOTTODO -// -// Created by JEONGEUN KIM on 2023/05/21. -// - -import Foundation - -// MARK: - WeekMissionResponseDTO - -struct WeekMissionResponseDTO: Codable { - let actionDate: String - let percentage: Float - - func toDate(dateString: String) -> Date? { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy.MM.dd" - dateFormatter.timeZone = TimeZone(identifier: "UTC") - if let date = dateFormatter.date(from: dateString) { - return date - } else { - return nil - } - } - - func convert() -> [Date: Float] { - guard let date = self.toDate(dateString: actionDate) else { return [:]} - return [date: percentage] - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 36b872dd..63679462 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -6,6 +6,7 @@ // import UIKit +import Combine import FSCalendar import Then @@ -101,17 +102,6 @@ extension AchievementViewController { $0.bottom.equalTo(scrollView.snp.bottom).inset(20) } } - - func requestMonthAPI(month: String) { - MissionAPI.shared.getAchieveCalendar(month: month) { [weak self] response in - - guard let self, let response = response, let data = response.data else { return } - - let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } - self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) - self.monthCalendar.reloadCollectionView() - } - } } extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { @@ -173,6 +163,17 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F extension AchievementViewController { + func requestMonthAPI(month: String) { + MissionAPI.shared.getAchieveCalendar(month: month) { [weak self] response in + + guard let self, let response = response, let data = response.data else { return } + + let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } + self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) + self.monthCalendar.reloadCollectionView() + } + } + private func getPercentage(for date: Date) -> Float? { let dateString = Utils.dateFormatterString(format: nil, date: date) From 7ee592a7fbe36fd41172209989884c12da8e89a5 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:47:21 +0900 Subject: [PATCH 04/21] [Merge] #241 - 'feat/#229' into 'feat/#241' --- .../Application/SceneDelegate.swift | 10 +- .../Coordinator/Common/Coordinator.swift | 65 +++++ .../Common/CoordinatorDelegate.swift | 12 + .../Coordinator/AchieveCoordinatorImpl.swift | 45 ++++ .../Coordinator/AppCoordinatorImpl.swift | 49 ++++ .../Coordinator/AuthCoordinatorImpl.swift | 105 ++++++++ .../Coordinator/HomecoordinatorImpl.swift | 117 +++++++++ .../Coordinator/MypageCoordinatorImpl.swift | 77 ++++++ .../Coordinator/UpdateCoordinatorImpl.swift | 186 ++++++++++++++ .../Factory/CoordinatorFactory.swift | 85 +++++++ .../Factory/ViewControllerFactory.swift | 235 ++++++++++++++++++ .../Protocol/AchieveCoordinator.swift | 13 + .../Coordinator/Protocol/AppCoordinator.swift | 12 + .../Protocol/AuthCoordinator.swift | 21 ++ .../Protocol/HomeCoordinator.swift | 23 ++ .../Protocol/MypageCoordinator.swift | 16 ++ .../Protocol/UpdateCoordinator.swift | 17 ++ .../Coordinator/Type/CoordinatorType.swift | 17 ++ .../Global/Extensions/UIViewController+.swift | 12 +- .../Network/Base/AuthInterceptor.swift | 10 +- .../Network/Base/NetworkConstant.swift | 14 -- .../Network/Base/NetworkResult.swift | 16 -- .../AchievementViewController.swift | 19 +- .../Views/DetailAchieveHeaderView.swift | 2 +- .../AddMissionViewController.swift | 24 +- .../Auth/AuthViewController.swift | 33 ++- .../CommonNotificationViewController.swift | 18 +- .../Modal/NottodoModalViewController.swift | 26 +- .../Presentation/Common/Utils.swift | 2 + .../Home/Cells/DetailHeaderView.swift | 1 - .../DetailCalendarViewController.swift | 29 ++- .../Home/ViewControllers/HomeDataSource.swift | 21 +- .../HomeDeleteViewController.swift | 26 +- .../ViewControllers/HomeViewController.swift | 66 ++--- .../MissionDetailViewController.swift | 78 +++--- .../MyInfoViewController.swift | 20 +- .../MyInfoAccountViewController.swift | 60 +++-- .../FifthOnboardingViewController.swift | 46 ++-- .../FourthOnboardingViewController.swift | 27 +- .../LogoOnboardingViewController.swift | 46 +++- .../OnboardingHeaderView.swift | 3 +- .../SecondOnboardingViewController.swift | 23 +- .../ThirdOnboardingViewController.swift | 32 ++- .../ValueOnboardingViewController.swift | 26 +- .../Cells/RecommendCollectionViewCell.swift | 9 +- .../RecommendViewController.swift | 56 +++-- .../RecommendActionCollectionViewCell.swift | 2 +- .../Cells/RecommendActionHeaderView.swift | 8 +- .../RecommendActionViewController.swift | 162 ++++++------ .../TabBar/TabBarController.swift | 36 ++- .../Presentation/TabBar/TabBarItem.swift | 2 +- .../UpdateCheckViewController.swift | 83 ++----- 52 files changed, 1679 insertions(+), 464 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/Coordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/CoordinatorDelegate.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AppCoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AuthCoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/HomecoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/MypageCoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/UpdateCoordinatorImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/CoordinatorFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AppCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AuthCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/HomeCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/MypageCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/UpdateCoordinator.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Type/CoordinatorType.swift delete mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift delete mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift index eed04f6c..a8e15df2 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift @@ -12,14 +12,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? static var shared: SceneDelegate? { UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate } + var appCoordinator: AppCoordinator? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.overrideUserInterfaceStyle = UIUserInterfaceStyle.light - let rootViewController = UpdateCheckViewController() - let navigationController = UINavigationController(rootViewController: rootViewController) + let navigationController = UINavigationController() + appCoordinator = AppCoordinatorImpl(navigationController, + coordinatorFactory: CoordinatorFactoryImpl(), viewControllerFactory: ViewControllerFactoryImpl() + ) + + appCoordinator?.start() + navigationController.isNavigationBarHidden = true window.rootViewController = navigationController window.makeKeyAndVisible() diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/Coordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/Coordinator.swift new file mode 100644 index 00000000..8268d094 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/Coordinator.swift @@ -0,0 +1,65 @@ +// +// Coordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +protocol Coordinator: AnyObject { + var delegate: CoordinatorDelegate? { get set } + var type: CoordinatorType { get } + var navigationController: UINavigationController { get set } + var childCoordinators: [Coordinator] { get set } + + func start() + func finish() + + init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) +} + +extension Coordinator { + func finish() { + childCoordinators.removeAll() + delegate?.didFinish(childCoordinator: self) + debugPrint(childCoordinators) + } + + func changeAnimation() { + let scenes = UIApplication.shared.connectedScenes + let windowScene = scenes.first as? UIWindowScene + let window = windowScene?.windows.first + + if let window { + UIView.transition( + with: window, + duration: 0.5, + options: .transitionCrossDissolve, + animations: nil + ) + } + } + + // MARK: - pop/dismiss는 공통으로 추출 + + /* + 현재 UINavigationController의 스택에서 가장 아래에 있는 루트뷰 컨트롤러까지 모든 뷰 컨트롤러를 제거하고, + 루트 뷰 컨트롤러를 화면에 표시하는 역할 + */ + func popToRootViewController() { + navigationController.popToRootViewController(animated: true) + } + + func popViewController() { + navigationController.popViewController(animated: true) + } + + func dismiss() { + navigationController.dismiss(animated: true) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/CoordinatorDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/CoordinatorDelegate.swift new file mode 100644 index 00000000..388fc799 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Common/CoordinatorDelegate.swift @@ -0,0 +1,12 @@ +// +// CoordinatorDelegate.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol CoordinatorDelegate: AnyObject { + func didFinish(childCoordinator: Coordinator) +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift new file mode 100644 index 00000000..e36d0316 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift @@ -0,0 +1,45 @@ +// +// AchieveCoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +final class AchieveCoordinatorImpl: AchieveCoordinator { + + weak var delegate: CoordinatorDelegate? + var navigationController: UINavigationController + var childCoordinators = [Coordinator]() + var type: CoordinatorType { .achievement } + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + } + + func start() { + showAchieveViewController() + } + + func showAchieveViewController() { + let viewController = viewControllerFactory.makeAchieveViewController(coordinator: self) + navigationController.setViewControllers([viewController], animated: false) + } + + func showAchieveDetailViewController(selectedDate: Date) { + let viewController = viewControllerFactory.makeAchieveDetailViewController(coordinator: self, date: selectedDate) + viewController.modalTransitionStyle = .crossDissolve + viewController.modalPresentationStyle = .overFullScreen + navigationController.present(viewController, animated: false) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AppCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AppCoordinatorImpl.swift new file mode 100644 index 00000000..531b773d --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AppCoordinatorImpl.swift @@ -0,0 +1,49 @@ +// +// AppCoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +final class AppCoordinatorImpl: NSObject, AppCoordinator { + weak var delegate: CoordinatorDelegate? + var type: CoordinatorType { .app } + var navigationController: UINavigationController + var childCoordinators = [Coordinator]() + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + private var tabBarController: UITabBarController? + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + } + + func start() { + showUpdateFlow() + } + + func showUpdateFlow() { + let updateCoordinator = coordinatorFactory.makeUpdateCoordinator(navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory) + updateCoordinator.delegate = self + updateCoordinator.start() + childCoordinators.append(updateCoordinator) + } +} + +extension AppCoordinatorImpl: CoordinatorDelegate { + func didFinish(childCoordinator: Coordinator) { + childCoordinators = childCoordinators.filter { $0.type != childCoordinator.type } + navigationController.viewControllers.removeAll() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AuthCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AuthCoordinatorImpl.swift new file mode 100644 index 00000000..94d8de91 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AuthCoordinatorImpl.swift @@ -0,0 +1,105 @@ +// +// OnboardingCoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/19/24. +// + +import UIKit + +final class AuthCoordinatorImpl: AuthCoordinator { + + weak var delegate: CoordinatorDelegate? + var type: CoordinatorType { .auth } + var navigationController: UINavigationController + var childCoordinators = [Coordinator]() + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + + deinit { + NotificationCenter.default.removeObserver(self) + } + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + + NotificationCenter.default.addObserver( + self, + selector: #selector(popToRoot), + name: NotificationCenterKey.refreshTokenHasExpired, + object: nil + ) + } + + func start() { + if KeychainUtil.getAccessToken().isEmpty { + showValueOnboardingViewController() + } else { + showSignUpViewController() + } + } + + func showValueOnboardingViewController() { + let viewController = viewControllerFactory.makeValueOnboardingViewController(coordinator: self) + changeAnimation() + navigationController.setViewControllers([viewController], animated: false) // 루트 뷰 컨트롤러 설정 + } + + func showLogoOnboardingViewController() { + let viewController = viewControllerFactory.makeLogoOnboardingViewController(coordinator: self) + navigationController.pushViewController(viewController, animated: false) // value -> logo + } + + func showSecondOnboardingViewController() { + let viewController = viewControllerFactory.makeSecondOnboardingViewController(coordinator: self) + changeAnimation() + navigationController.pushViewController(viewController, animated: false) // logo -> second + } + + func showThirdOnboardingViewController() { + let viewController = viewControllerFactory.makeThirdOnboardingViewController(coordinator: self) + changeAnimation() + navigationController.pushViewController(viewController, animated: false) // logo -> third + } + + func showFourthOnboardingViewController() { + let viewController = viewControllerFactory.makeFourthOnboardingViewController(coordinator: self) + changeAnimation() + navigationController.pushViewController(viewController, animated: false) // third -> fourth + } + + func showFifthOnboardingViewController() { + let viewController = viewControllerFactory.makeFifthOnboardingViewController(coordinator: self) + changeAnimation() + navigationController.pushViewController(viewController, animated: false) // fourth -> fifth + } + + func showSignUpViewController() { // fifth -> root 변경 -> signup + let viewController = viewControllerFactory.makeAuthViewController(coordinator: self) + changeAnimation() + navigationController.setViewControllers([viewController], animated: false) // 루트 뷰 컨트롤러 설정 + } + + func showNotificationViewController(completion: @escaping () -> Void) { + let viewController = viewControllerFactory.makeNotificationDialogViewController(coordinator: self, completion: completion) + navigationController.setViewControllers([viewController], animated: false) + } + + func connectHomeCoordinator() { + navigationController.dismiss(animated: true) { [unowned self] in + finish() + } + } + + @objc + func popToRoot(_ notification: Notification) { + showSignUpViewController() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/HomecoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/HomecoordinatorImpl.swift new file mode 100644 index 00000000..0f5aca80 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/HomecoordinatorImpl.swift @@ -0,0 +1,117 @@ +// +// HomecoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +final class HomeCoordinatorImpl: HomeCoordinator { + + weak var delegate: CoordinatorDelegate? + var navigationController: UINavigationController + var recommendNavigationController: UINavigationController? + var childCoordinators = [Coordinator]() + var type: CoordinatorType { .home } + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + } + + func start() { + showHomeViewController() + } + + func showHomeViewController() { + let viewController = viewControllerFactory.makeHomeViewController(coordinator: self) + navigationController.pushViewController(viewController, animated: false) + } + + func showPopupViewController(completion: @escaping () -> Void) { + let viewController = viewControllerFactory.makePopupViewController(coordinator: self, completion: completion) + viewController.modalPresentationStyle = .overFullScreen + viewController.modalTransitionStyle = .crossDissolve + navigationController.present(viewController, animated: true) + } + + func showMissionDetailViewController(id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) { + let viewController = viewControllerFactory.makeMissionDetailViewController(coordinator: self, id: id, deleteClosure: deleteClosure, moveDateClosure: moveDateClosure) + viewController.modalPresentationStyle = .overFullScreen + navigationController.present(viewController, animated: true) + } + + func showSelectDateViewController(data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) { + if let presentedViewController = navigationController.presentedViewController { + let viewController = viewControllerFactory.makeSelectDateViewController(coordinator: self, data: data, id: id, moveDateClosure: moveDateClosure) + viewController.modalTransitionStyle = .crossDissolve + viewController.modalPresentationStyle = .overFullScreen + presentedViewController.present(viewController, animated: false) + } + } + + func showModifyViewController(id: Int, type: MissionType) { + self.dismiss() + let viewController = viewControllerFactory.makeModifyViewController(coordinator: self, id: id, type: type) + viewController.modalPresentationStyle = .fullScreen // view will appear 동작 + navigationController.present(viewController, animated: true) + } + + func showAddViewController(data: AddMissionData, type: MissionType) { + self.dismiss() + let viewController = viewControllerFactory.makeAddViewController(coordinator: self, data: data, type: type) + viewController.modalPresentationStyle = .fullScreen // view will appear 동작 + navigationController.present(viewController, animated: true) + } + + func showRecommendViewController(selectedDate: String) { + let viewController = viewControllerFactory.makeRecommendViewController(coordinator: self, date: selectedDate) + let recommendNavigationController = UINavigationController(rootViewController: viewController) + recommendNavigationController.setNavigationBarHidden(true, animated: false) + recommendNavigationController.modalPresentationStyle = .fullScreen + navigationController.present(recommendNavigationController, animated: true) + self.recommendNavigationController = recommendNavigationController + } + + func showRecommendDetailViewController(actionData: RecommendActionData) { + let viewController = viewControllerFactory.makeRecommendDetailViewController(coordinator: self, data: actionData) + viewController.modalPresentationStyle = .fullScreen + recommendNavigationController?.navigationBar.isHidden = true + recommendNavigationController?.pushViewController(viewController, animated: true) + } + + func showDeleteViewController(completion: @escaping () -> Void) { + let presentedViewController = navigationController.presentedViewController != nil ? navigationController.presentedViewController : navigationController + let viewController = viewControllerFactory.makeDeleteViewController(coordinator: self, completion: completion) + viewController.modalPresentationStyle = .overFullScreen + viewController.modalTransitionStyle = .crossDissolve + presentedViewController?.present(viewController, animated: true) + } + + func dismissLastPresentedViewController() { + let presentedViewController = navigationController.presentedViewController != nil ? navigationController.presentedViewController : navigationController + presentedViewController?.dismiss(animated: true, completion: nil) + } +} + +extension HomeCoordinatorImpl { + + func popViewController() { + recommendNavigationController?.popViewController(animated: true) + } + + func dismissRecommendViewcontroller() { + recommendNavigationController?.dismiss(animated: true, completion: { [weak self] in + self?.recommendNavigationController?.viewControllers.removeAll() + }) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/MypageCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/MypageCoordinatorImpl.swift new file mode 100644 index 00000000..03bb656d --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/MypageCoordinatorImpl.swift @@ -0,0 +1,77 @@ +// +// MypageCoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +final class MypageCoordinatorImpl: MypageCoordinator { + + weak var delegate: CoordinatorDelegate? + var type: CoordinatorType { .mypage } + var navigationController: UINavigationController + var childCoordinators = [Coordinator]() + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + } + + func start() { + showMyInfoViewController() + } + + func showMyInfoViewController() { + let viewController = viewControllerFactory.makeMyInfoViewController(coordinator: self) + navigationController.setViewControllers([viewController], animated: true) + } + + func showMyInfoAccountViewController() { + let viewController = viewControllerFactory.makeMyInfoAccountViewController(coordinator: self) + viewController.hidesBottomBarWhenPushed = true + navigationController.pushViewController(viewController, animated: true) + } + + func showWithdrawViewController() { + let viewController = viewControllerFactory.makeWithdrawViewController(coordinator: self) + viewController.modalTransitionStyle = .crossDissolve + viewController.modalPresentationStyle = .overFullScreen + navigationController.present(viewController, animated: true) + } + + func showLogoutAlertController(completion: @escaping () -> Void) { + let logoutAlert = UIAlertController(title: I18N.logoutAlertTitle, message: I18N.logoutAlertmessage, preferredStyle: UIAlertController.Style.alert) + let logoutAction = UIAlertAction(title: I18N.logout, style: UIAlertAction.Style.default, handler: { _ in + completion() + }) + let cancelAlert = UIAlertAction(title: I18N.cancel, style: UIAlertAction.Style.default, handler: nil) + logoutAlert.addAction(cancelAlert) + logoutAlert.addAction(logoutAction) + navigationController.present(logoutAlert, animated: true) + } + + func connectAuthCoordinator(type: ViewType) { + navigationController.dismiss(animated: true) { [weak self] in + self?.finish() + switch type { + case .quitSurvey: + KeychainUtil.removeUserInfo() + case .logout: + UserDefaults.standard.removeObject(forKey: DefaultKeys.accessToken) + UserDefaults.standard.removeObject(forKey: DefaultKeys.socialToken) + default: + break + } + } + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/UpdateCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/UpdateCoordinatorImpl.swift new file mode 100644 index 00000000..14775dbd --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/UpdateCoordinatorImpl.swift @@ -0,0 +1,186 @@ +// +// UpdateCoordinatorImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +final class UpdateCoordinatorImpl: UpdateCoordinator { + + weak var delegate: CoordinatorDelegate? + var type: CoordinatorType { .update } + var navigationController: UINavigationController + var childCoordinators = [Coordinator]() + + private let coordinatorFactory: CoordinatorFactory + private let viewControllerFactory: ViewControllerFactory + private var tabBarController: UITabBarController? + + required init( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) { + self.navigationController = navigationController + self.coordinatorFactory = coordinatorFactory + self.viewControllerFactory = viewControllerFactory + + } + + enum Constant { + static let appstoreURL: String = "itms-apps://itunes.apple.com/app/\(Bundle.main.appleId)" + } + + func start() { + showUpdateViewController() + } + + func showUpdateViewController() { + let viewController = viewControllerFactory.makeUpdateCheckViewController(coordinator: self) + navigationController.setViewControllers([viewController], animated: true) + } + + func showUpdateAlertController() { + let alertController = UIAlertController( + title: I18N.update, + message: I18N.updateAlert, + preferredStyle: .alert + ) + + let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in + // App Store로 이동 + if let appStoreURL = URL(string: Constant.appstoreURL) { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: { [weak self] _ in + self?.changeMainViewController() + }) + } + } + + let cancelAction = UIAlertAction(title: I18N.later, style: .default) { _ in + // App Store로 이동 + if let appStoreURL = URL(string: Constant.appstoreURL) { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: { [weak self] _ in + self?.changeMainViewController() + }) + } + } + + alertController.addAction(updateAction) + alertController.addAction(cancelAction) + + navigationController.present(alertController, animated: true) + } + + func showForceUpdateAlertController(newVersion: String) { + let alertController = UIAlertController( + title: I18N.update, + message: I18N.forceUpdateAlert(newVersion: newVersion), + preferredStyle: .alert + ) + + let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in + // App Store로 이동 + if let appStoreURL = URL(string: Constant.appstoreURL) { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) + } + } + alertController.addAction(updateAction) + navigationController.present(alertController, animated: true) + } + + func changeMainViewController() { + + if KeychainUtil.getAccessToken().isEmpty { + self.showAuthFlow() + } else { + self.showTabFlow() + } + } + + func showTabFlow() { + let (tabBarController, navigationControllers) = viewControllerFactory.makeTabBarController(navigationController) + self.tabBarController = tabBarController + + tabBarController.setViewControllers(navigationControllers, animated: false) + + let coordinators = navigationControllers.compactMap(makeTabCoordinator) + coordinators.forEach { + $0.delegate = self + childCoordinators.append($0) + $0.start() + } + + navigationController.setNavigationBarHidden(true, animated: false) + navigationController.setViewControllers([tabBarController], animated: false) + } + + func showAuthFlow() { + let authCoordinator = coordinatorFactory.makeAuthCoordinator(navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory) + authCoordinator.delegate = self + authCoordinator.start() + childCoordinators.append(authCoordinator) + } +} + +// MARK: - Make TabBar + +extension UpdateCoordinatorImpl { + @discardableResult + func makeTabCoordinator(_ navigationController: UINavigationController) -> Coordinator? { + let tabType = TabBarItemType(rawValue: navigationController.tabBarItem.tag) + + switch tabType { + case .home: + let coordinator = coordinatorFactory.makeHomeCoordinator( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + + case .achievement: + let coordinator = coordinatorFactory.makeAchieveCoordinator( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + + case .mypage: + let coordinator = coordinatorFactory.makeMypageCoordinator( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + + default: + return nil + } + } +} + +extension UpdateCoordinatorImpl: CoordinatorDelegate { + /* update의 child로 탭바(home, mypage, achieve), auth가 존재함 + 각각의 child에서 delegate?didfinish를 호출하면 여기로 호출이됨. + delegate는 부모와 연결해주는 역할 + */ + func didFinish(childCoordinator: Coordinator) { + + childCoordinators = childCoordinators.filter { $0.type != childCoordinator.type } + navigationController.viewControllers.removeAll() + + switch childCoordinator.type { + case .home, .achievement, .auth: + showTabFlow() + case .mypage: + showAuthFlow() + default: + showUpdateViewController() + } + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/CoordinatorFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/CoordinatorFactory.swift new file mode 100644 index 00000000..f2095d20 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/CoordinatorFactory.swift @@ -0,0 +1,85 @@ +// +// CoordinatorFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +/* + CoordinatorFactory + - description: 코디네이터 생성을 담당 + */ + +protocol CoordinatorFactory {} +final class CoordinatorFactoryImpl: CoordinatorFactory {} + +extension CoordinatorFactory { + func makeUpdateCoordinator( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) -> UpdateCoordinator { + let coordinator = UpdateCoordinatorImpl( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + } + + func makeAuthCoordinator( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) -> AuthCoordinator { + let coordinator = AuthCoordinatorImpl( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + } + + func makeHomeCoordinator( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) -> HomeCoordinator { + let coordinator = HomeCoordinatorImpl( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + } + + func makeAchieveCoordinator( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) + -> AchieveCoordinator { + let coordinator = AchieveCoordinatorImpl( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + } + + func makeMypageCoordinator( + _ navigationController: UINavigationController, + coordinatorFactory: CoordinatorFactory, + viewControllerFactory: ViewControllerFactory + ) + -> MypageCoordinator { + let coordinator = MypageCoordinatorImpl( + navigationController, + coordinatorFactory: coordinatorFactory, + viewControllerFactory: viewControllerFactory + ) + return coordinator + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift new file mode 100644 index 00000000..1ffa93d8 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift @@ -0,0 +1,235 @@ +// +// ViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import UIKit + +protocol ViewControllerFactory: UpdateFlowcontrollerFactory, AuthFlowControllerFactory, HomeFlowControllerFactory, MyPageFlowControllerFactory, AchieveFlowControllerFactory, TabBarControllerFactory, AuthFlowControllerFactory {} + +protocol UpdateFlowcontrollerFactory { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController +} + +protocol AuthFlowControllerFactory { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController + +} + +protocol HomeFlowControllerFactory { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController + func makeModifyViewController(coordinator: HomeCoordinator, id: Int, type: MissionType) -> AddMissionViewController + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController +} + +protocol MyPageFlowControllerFactory { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController +} + +protocol AchieveFlowControllerFactory { + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController +} + +protocol TabBarControllerFactory { + func makeTabBarController(_ navigationController: UINavigationController) -> (UITabBarController, [UINavigationController]) +} + +final class ViewControllerFactoryImpl: ViewControllerFactory {} + +// update +extension ViewControllerFactoryImpl { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController { + let viewController = UpdateCheckViewController(coordinator: coordinator) + return viewController + } +} +// onboarding +extension ViewControllerFactoryImpl { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController { + let viewController = ValueOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController { + let viewController = LogoOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController { + let viewController = SecondOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController { + let viewController = ThirdOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController { + let viewController = FourthOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController { + let viewController = FifthOnboardingViewController(coordinator: coordinator) + return viewController + } +} +// auth +extension ViewControllerFactoryImpl { + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController { + let viewController = AuthViewController(coordinator: coordinator) + return viewController + } + + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController { + let viewController = NotificationDialogViewController() + viewController.buttonHandler = { + completion() + } + return viewController + } +} +// home +extension ViewControllerFactoryImpl { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController { + let viewController = CommonNotificationViewController(coordinator: coordinator) + viewController.tapCloseButton = { + completion() + } + return viewController + } + + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController { + let viewController = HomeViewController(coordinator: coordinator) + return viewController + } +} + +// add mission +extension ViewControllerFactoryImpl { + + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController { + let viewController = RecommendViewController(coordinator: coordinator) + viewController.setSelectDate(date) + return viewController + } + + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController { + let viewController = RecommendActionViewController(coordinator: coordinator) + viewController.actionHeaderData = data + return viewController + } + + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setNottodoLabel(data.nottodo ?? "") + viewController.setSituationLabel(data.situation ?? "") + viewController.setActionLabel(data.action ?? "") + viewController.setDate(data.date) + + return viewController + } +} +// home detail +extension ViewControllerFactoryImpl { + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController { + let viewController = HomeDeleteViewController(coordinator: coordinator) + viewController.deleteClosure = { + completion() + } + return viewController + } + + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController { + let viewController = MissionDetailViewController(coordinator: coordinator) + viewController.userId = id + viewController.deleteClosure = { + deleteClosure() + } + viewController.moveDateClosure = { date in + moveDateClosure(date) + } + return viewController + } + + func makeModifyViewController(coordinator: HomeCoordinator, id: Int = 0, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setMissionId(id) + return viewController + } + + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController { + let viewController = DetailCalendarViewController(coordinator: coordinator) + viewController.detailModel = data + viewController.userId = id + viewController.movedateClosure = { date in + moveDateClosure(date) + } + return viewController + } +} +// mypage +extension ViewControllerFactoryImpl { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController { + let viewController = MyInfoViewController(coordinator: coordinator) + return viewController + } + + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController { + let viewController = MyInfoAccountViewController(coordinator: coordinator) + return viewController + } + + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController { + let viewController = NottodoModalViewController(coordinator: coordinator) + return viewController + } +} +// achieve +extension ViewControllerFactoryImpl { + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { + let viewController = AchievementViewController(coordinator: coordinator) + return viewController + } + + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController { + let missionAPI = DefaultMissionAPI() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, missionManager: manager) + viewModel.selectedDate(date) + let viewController = DetailAchievementViewController(viewModel: viewModel) + return viewController + } +} +// tabbar +extension ViewControllerFactoryImpl { + func makeTabBarController(_: UINavigationController) -> (UITabBarController, [UINavigationController]) { + let tabBarController = TabBarController() + let navigationControllers = tabBarController.setTabBarItems().map(makeNavigationController) + + return (tabBarController, navigationControllers) + } + + func makeNavigationController(_ tabBarItem: UITabBarItem) -> UINavigationController { + let navigationController = UINavigationController() + navigationController.tabBarItem = tabBarItem + navigationController.setNavigationBarHidden(true, animated: false) + return navigationController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift new file mode 100644 index 00000000..cd5ca4c9 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift @@ -0,0 +1,13 @@ +// +// AchieveCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol AchieveCoordinator: Coordinator { + func showAchieveViewController() + func showAchieveDetailViewController(selectedDate: Date) +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AppCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AppCoordinator.swift new file mode 100644 index 00000000..52b0c797 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AppCoordinator.swift @@ -0,0 +1,12 @@ +// +// AppCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol AppCoordinator: Coordinator { + func showUpdateFlow() +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AuthCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AuthCoordinator.swift new file mode 100644 index 00000000..713b019e --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AuthCoordinator.swift @@ -0,0 +1,21 @@ +// +// OnboardingCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/19/24. +// + +import Foundation + +protocol AuthCoordinator: Coordinator { + func showValueOnboardingViewController() + func showLogoOnboardingViewController() + func showSecondOnboardingViewController() + func showThirdOnboardingViewController() + func showFourthOnboardingViewController() + func showFifthOnboardingViewController() + func connectHomeCoordinator() + func showSignUpViewController() + func showNotificationViewController(completion: @escaping () -> Void) + func popViewController() +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/HomeCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/HomeCoordinator.swift new file mode 100644 index 00000000..494deccc --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/HomeCoordinator.swift @@ -0,0 +1,23 @@ +// +// HomeCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol HomeCoordinator: Coordinator { + func showHomeViewController() + func showPopupViewController(completion: @escaping () -> Void) + func showMissionDetailViewController(id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) + func showSelectDateViewController(data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) + func showModifyViewController(id: Int, type: MissionType) + func showAddViewController(data: AddMissionData, type: MissionType) + func showRecommendViewController(selectedDate: String) + func showRecommendDetailViewController(actionData: RecommendActionData) + func showDeleteViewController(completion: @escaping () -> Void) + func popViewController() + func dismissRecommendViewcontroller() + func dismissLastPresentedViewController() +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/MypageCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/MypageCoordinator.swift new file mode 100644 index 00000000..cc0b59bb --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/MypageCoordinator.swift @@ -0,0 +1,16 @@ +// +// MypageCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol MypageCoordinator: Coordinator { + func showMyInfoViewController() + func showMyInfoAccountViewController() + func showWithdrawViewController() + func showLogoutAlertController(completion: @escaping () -> Void) // action button 클릭시 이벤트 + func connectAuthCoordinator(type: ViewType) // type에 따라서 userdefault 비워줌 +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/UpdateCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/UpdateCoordinator.swift new file mode 100644 index 00000000..1ab2a198 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/UpdateCoordinator.swift @@ -0,0 +1,17 @@ +// +// UpdateCoordinator.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +protocol UpdateCoordinator: Coordinator { + func showUpdateViewController() + func showUpdateAlertController() + func showForceUpdateAlertController(newVersion: String) + func changeMainViewController() + func showTabFlow() + func showAuthFlow() +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Type/CoordinatorType.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Type/CoordinatorType.swift new file mode 100644 index 00000000..b3cba5d7 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Type/CoordinatorType.swift @@ -0,0 +1,17 @@ +// +// CoordinatorType.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2/18/24. +// + +import Foundation + +@frozen enum CoordinatorType { + case app + case update + case auth + case home + case mypage + case achievement +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift index cd67869f..ab241846 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIViewController+.swift @@ -86,17 +86,7 @@ extension UIViewController { open override func touchesBegan(_ touches: Set, with event: UIEvent?) { self.view.endEditing(true) } - - @objc - func dismissViewController() { - self.dismiss(animated: true) - } - - @objc - func popViewController() { - self.navigationController?.popToRootViewController(animated: true) - } - + func hideKeyboardWhenTappedAround() { let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard)) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift index 64f21c98..69bfa6c5 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/AuthInterceptor.swift @@ -9,12 +9,16 @@ import Foundation import Alamofire +enum NotificationCenterKey { + static let refreshTokenHasExpired = Notification.Name("refreshTokenHasExpired") +} + final class AuthInterceptor: RequestInterceptor { static let shared = AuthInterceptor() private init() {} - + func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 @@ -24,7 +28,9 @@ final class AuthInterceptor: RequestInterceptor { } DispatchQueue.main.async { - SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) + // token 만료 시 -> auth 화면 전환 + NotificationCenter.default.post(name: NotificationCenterKey.refreshTokenHasExpired, object: nil) } + completion(.doNotRetry) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift deleted file mode 100644 index 97f80f4d..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// NetworkConstant.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -struct NetworkConstant { - static let noTokenHeader = ["Content-Type": "application/json"] - static var hasTokenHeader = ["Content-Type": "application/json", - "Authorization": "\(KeychainUtil.getAccessToken())"] -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift deleted file mode 100644 index 2ead150d..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// NetworkResult.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -enum NetworkResult { - case success(T) // 서버 통신 성공했을 때, - case requestErr(T) // 요청 에러 발생했을 때, - case pathErr // 경로 에러 발생했을 때, - case serverErr // 서버의 내부적 에러가 발생했을 때, - case networkFail // 네트워크 연결 실패했을 때 -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 63679462..f4226303 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -16,6 +16,7 @@ final class AchievementViewController: UIViewController { // MARK: - Properties + private weak var coordinator: AchieveCoordinator? private var currentPage = Date() private var dataSource: [String: Float] = [:] @@ -28,6 +29,16 @@ final class AchievementViewController: UIViewController { private let monthCalendar = CalendarView(scope: .month) private let statisticsView = StatisticsView() + // MARK: - init + init(coordinator: AchieveCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { @@ -121,11 +132,7 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F calendar.appearance.titleSelectionColor = .white let dateString = Utils.dateFormatterString(format: "yyyy-MM-dd", date: date) if self.dataSource.contains(where: { $0.key == dateString }) { - let vc = DetailAchievementViewController() - vc.selectedDate = date - vc.modalPresentationStyle = .overFullScreen - vc.modalTransitionStyle = .crossDissolve - present(vc, animated: false) + coordinator?.showAchieveDetailViewController(selectedDate: date) } } @@ -170,7 +177,7 @@ extension AchievementViewController { let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) - self.monthCalendar.reloadCollectionView() + self.monthCalendar.calendar.collectionView.reloadData() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift index a638dd51..09df3f39 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift @@ -50,7 +50,7 @@ extension DetailAchieveHeaderView { private func setLayout() { addSubview(dateLabel) - + dateLabel.snp.makeConstraints { $0.top.equalToSuperview() $0.centerX.equalToSuperview() diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift index 17d99840..d42a9bf7 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift @@ -42,6 +42,8 @@ final class AddMissionViewController: UIViewController { } } + private weak var coordinator: HomeCoordinator? + // MARK: - UI Components private let navigationView = UIView() @@ -53,6 +55,17 @@ final class AddMissionViewController: UIViewController { private lazy var addMissionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -137,7 +150,7 @@ extension AddMissionViewController { dismissButton.do { $0.setBackgroundImage(.icDelete, for: .normal) - $0.addTarget(self, action: #selector(self.popViewController), for: .touchUpInside) + $0.addTarget(self, action: #selector(popVC), for: .touchUpInside) } navigationTitle.do { @@ -254,6 +267,11 @@ extension AddMissionViewController { ) } } + + @objc + private func popVC() { + coordinator?.dismiss() + } } // MARK: API func @@ -275,7 +293,7 @@ extension AddMissionViewController { action: actions ?? [] ) ) - self.popViewController() + self.coordinator?.dismiss() default: let toastMessage = self.htmlToString(response.message ?? "")?.string ?? "" self.checkToastMessage(toastMessage) @@ -299,7 +317,7 @@ extension AddMissionViewController { situation: self.nottodoInfoList[2], action: self.nottodoInfoList[3]) ) - self.popViewController() + self.coordinator?.dismiss() default: let toastMessage = self.htmlToString(response.message ?? "")?.string ?? "" self.checkToastMessage(toastMessage) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift index d4e77649..7cf0ef57 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift @@ -19,6 +19,10 @@ import KakaoSDKUser final class AuthViewController: UIViewController { + // MARK: - Property + + private weak var coordinator: AuthCoordinator? + // MARK: - UI Components private var loginMainLabel = UILabel() @@ -34,8 +38,18 @@ final class AuthViewController: UIViewController { private var conditionButton = UIButton() private var personalInfoButton = UIButton() + // MARK: - init + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle - + override func viewDidLoad() { super.viewDidLoad() AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.viewSignIn) @@ -55,7 +69,7 @@ extension AuthViewController { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = $0.font.lineHeight * 0.2 - + let attributedText = NSAttributedString(string: $0.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]) $0.attributedText = attributedText } @@ -68,7 +82,7 @@ extension AuthViewController { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = $0.font.lineHeight * 0.2 - + let attributedText = NSAttributedString(string: $0.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]) $0.attributedText = attributedText } @@ -155,7 +169,7 @@ extension AuthViewController { } } - + // MARK: - @objc Methods @objc func moreButtonTapped() { @@ -164,7 +178,7 @@ extension AuthViewController { @objc func kakaoLoginButtonClicked() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickSignIn(provider: "kakao")) - + if UserApi.isKakaoTalkLoginAvailable() { kakaoLoginWithApp() } else { @@ -175,7 +189,7 @@ extension AuthViewController { @objc func appleLoginButtonClicked() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickSignIn(provider: "apple")) - + let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] @@ -250,7 +264,7 @@ extension AuthViewController { func presentToHomeViewController() { DispatchQueue.main.async { - SceneDelegate.shared?.changeRootViewControllerTo(TabBarController()) + self.coordinator?.connectHomeCoordinator() } } @@ -267,11 +281,10 @@ extension AuthViewController { func showNotiDialogView() { DispatchQueue.main.async { - let notiDialogViewController = NotificationDialogViewController() - notiDialogViewController.buttonHandler = { + self.coordinator?.showNotificationViewController { [weak self] in + guard let self else { return } self.requestNotification() } - SceneDelegate.shared?.changeRootViewControllerTo(notiDialogViewController) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift index c34f6e29..5a046d73 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift @@ -16,7 +16,8 @@ final class CommonNotificationViewController: UIViewController { // MARK: - Property var tapCloseButton: (() -> Void)? - + private weak var coordinator: HomeCoordinator? + // MARK: - UI Components private let backgroundView = UIView() @@ -31,6 +32,17 @@ final class CommonNotificationViewController: UIViewController { private let closeButton = UIButton() private let deprecatedButton = UIButton() + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - View Life Cycle override func viewDidLoad() { @@ -189,7 +201,7 @@ extension CommonNotificationViewController { @objc func didCancelButtonTap() { self.tapCloseButton?() - dismissViewController() + coordinator?.dismiss() } @objc @@ -203,6 +215,6 @@ extension CommonNotificationViewController { extension CommonNotificationViewController: SFSafariViewControllerDelegate { func safariViewControllerDidFinish(_ controller: SFSafariViewController) { - dismissViewController() + coordinator?.dismiss() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/NottodoModalViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/NottodoModalViewController.swift index dfab9ef6..b89d5214 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/NottodoModalViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/NottodoModalViewController.swift @@ -13,20 +13,21 @@ import SafariServices enum ViewType { case quit case quitSurvey + case logout } final class NottodoModalViewController: UIViewController { // MARK: - Properties + private weak var coordinator: MypageCoordinator? + private var viewType: ViewType? = .quit { didSet { setUI() setLayout() } } - var dimissAction: (() -> Void)? - var pushToRootAction: (() -> Void)? // MARK: - UI Components @@ -35,6 +36,17 @@ final class NottodoModalViewController: UIViewController { private let quitView = QuitModalView() private lazy var safariViewController = SFSafariViewController(url: URL(string: MyInfoURL.commonAlarmModal.url)!) + // MARK: - init + + init(coordinator: MypageCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -51,7 +63,7 @@ final class NottodoModalViewController: UIViewController { let location = touch.location(in: self.view) if !modalView.frame.contains(location) { - dismiss(animated: true) + coordinator?.dismiss() } } } @@ -102,8 +114,7 @@ extension NottodoModalViewController: ModalDelegate { } func modalDismiss() { - dismiss(animated: true) - self.dimissAction?() + coordinator?.dismiss() // 탈퇴 alert 취소 } } @@ -113,7 +124,6 @@ extension NottodoModalViewController { kakaoWithdrawal() } AuthAPI.shared.withdrawalAuth { _ in - KeychainUtil.removeUserInfo() AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.AccountInfo.completeWithdrawal) } } @@ -131,7 +141,7 @@ extension NottodoModalViewController { extension NottodoModalViewController: SFSafariViewControllerDelegate { func safariViewControllerDidFinish(_ controller: SFSafariViewController) { - dismissViewController() - self.pushToRootAction?() + controller.delegate = nil + coordinator?.connectAuthCoordinator(type: .quitSurvey) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift index 650c0290..cb953f00 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift @@ -86,3 +86,5 @@ final class Utils { return version } } + + diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift index 666b681a..0bb6c192 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/DetailHeaderView.swift @@ -34,7 +34,6 @@ final class DetailHeaderView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - } // MARK: - Method diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 09555328..90f40c2f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -29,13 +29,24 @@ final class DetailCalendarViewController: UIViewController { private lazy var safeArea = self.view.safeAreaLayoutGuide private lazy var today: Date = { return Date() }() - + private weak var coordinator: HomeCoordinator? + // MARK: - UI Components private let monthCalendar = CalendarView(scope: .month) private let completeButton = UIButton() private let subLabel = UILabel() + // MARK: - init + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { @@ -60,7 +71,7 @@ final class DetailCalendarViewController: UIViewController { let location = touch.location(in: self.view) if !monthCalendar.frame.contains(location) { - self.dismiss(animated: true) + coordinator?.dismissLastPresentedViewController() } } } @@ -68,7 +79,7 @@ final class DetailCalendarViewController: UIViewController { // MARK: - Methods extension DetailCalendarViewController { - + private func setUI() { view.backgroundColor = .black.withAlphaComponent(0.6) @@ -76,7 +87,7 @@ extension DetailCalendarViewController { $0.setTitle(I18N.detailComplete, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 16) $0.addTarget(self, action: #selector(completeBtnTapped(sender:)), for: .touchUpInside) - + } monthCalendar.do { @@ -114,13 +125,13 @@ extension DetailCalendarViewController { $0.trailing.equalToSuperview().inset(16) $0.size.equalTo(CGSize(width: 44, height: 35)) } - + monthCalendar.snp.makeConstraints { $0.centerY.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea).inset(15) $0.height.equalTo((Numbers.width-30)*1.2) } - + subLabel.snp.makeConstraints { $0.bottom.equalToSuperview().inset(25) $0.left.equalToSuperview().offset(17) @@ -163,10 +174,10 @@ extension DetailCalendarViewController: FSCalendarDelegate, FSCalendarDataSource let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: currentDate) currentDate = Calendar.current.startOfDay(for: nextDay!) } - + let formattedDatesArray = datesArray.map { Utils.dateFormatterString(format: "yyyy.MM.dd", date: $0) } let formattedDate = Utils.dateFormatterString(format: "yyyy.MM.dd", date: date) - + return (!formattedDatesArray.contains(formattedDate) || !invalidDate.contains(formattedDate)) && Utils.calendarSelected(today: today, date: date) } @@ -206,7 +217,7 @@ extension DetailCalendarViewController { switch statusCode { case 200..<204: self.setUI() - self.dismiss(animated: true) + self.coordinator?.dismiss() AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.SelectDate.closeAnotherDayModal) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy.MM.dd" diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift index a6e23db0..9f7e40f6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDataSource.swift @@ -26,7 +26,7 @@ final class HomeDataSource { case mission, empty } - + enum Item: Hashable { case mission(DailyMissionResponseDTO) @@ -79,8 +79,8 @@ final class HomeDataSource { private func createMissionCellRegistration() -> CellRegistration { - return CellRegistration { cell, _, item in - guard let missionItem = self.getMissionItem(from: item) else { return } + return CellRegistration { [weak self] cell, _, item in + guard let missionItem = self?.getMissionItem(from: item) else { return } cell.configure(model: missionItem) @@ -134,13 +134,12 @@ final class HomeDataSource { private func createLayout() -> UICollectionViewLayout { - let layout = UICollectionViewCompositionalLayout { sectionIndex, env in - - guard let section = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] else { return nil } + let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, env in + guard let section = self?.dataSource?.snapshot().sectionIdentifiers[sectionIndex] else { return nil } switch section { case .mission: - return self.missionSection(env: env) + return self?.missionSection(env: env) case .empty: return CompositionalLayout.vertical(count: 1, edge: .init(top: 30, leading: 0, bottom: 0, trailing: 0)) } @@ -171,7 +170,7 @@ final class HomeDataSource { let indexPath = result.index let data = result.mission - let deleteAction = UIContextualAction(style: .normal, title: "") { [unowned self] _, _, completion in + let deleteAction = UIContextualAction(style: .normal, title: "") { [weak self] _, _, completion in AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "home", title: data.title, @@ -179,15 +178,15 @@ final class HomeDataSource { goal: "", action: [])) - self.modalDelegate?.deleteMission(index: indexPath, id: data.id) + self?.modalDelegate?.deleteMission(index: indexPath, id: data.id) completion(true) } - let modifyAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in + let modifyAction = UIContextualAction(style: .normal, title: "") { [weak self] _, _, completionHandler in AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "home")) - self.modalDelegate?.modifyMission(id: data.id, type: .update) + self?.modalDelegate?.modifyMission(id: data.id, type: .update) completionHandler(true) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift index 72b94a45..df985b01 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeDeleteViewController.swift @@ -16,12 +16,24 @@ final class HomeDeleteViewController: UIViewController { var deleteClosure: (() -> Void)? + private weak var coordinator: HomeCoordinator? + private lazy var safeArea = self.view.safeAreaLayoutGuide // MARK: - UI Components private let deleteModalView = DeleteModalView() + // MARK: - init + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -36,8 +48,8 @@ final class HomeDeleteViewController: UIViewController { let touch = touches.first! let location = touch.location(in: self.view) - if !view.frame.contains(location) { - dismiss(animated: true) + if !deleteModalView.frame.contains(location) { + coordinator?.dismissLastPresentedViewController() } } } @@ -49,12 +61,14 @@ extension HomeDeleteViewController { view.backgroundColor = .black.withAlphaComponent(0.6) deleteModalView.do { - $0.deleteClosure = { + $0.deleteClosure = { [weak self] in + guard let self else { return } self.deleteClosure?() - self.dismiss(animated: true) } - $0.cancelClosure = { - self.dismiss(animated: true) + + $0.cancelClosure = { [weak self] in + guard let self else { return } + self.coordinator?.dismissLastPresentedViewController() } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index b6dea45b..5e3a6e24 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -15,6 +15,7 @@ final class HomeViewController: UIViewController { // MARK: - Properties + private weak var coordinator: HomeCoordinator? private var missionList: [DailyMissionResponseDTO] = [] private var calendarDataSource: [String: Float] = [:] @@ -34,6 +35,17 @@ final class HomeViewController: UIViewController { private let weekCalendar = CalendarView(scope: .week) private let addButton = UIButton() + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { @@ -116,10 +128,8 @@ extension HomeViewController: WeekCalendarDelegate { @objc func addBtnTapped(_sender: UIButton) { - let nextViewController = RecommendViewController() - nextViewController.setSelectDate(Utils.dateFormatterString(format: "yyyy.MM.dd", date: selectedDate ?? Date())) - - Utils.push(navigationController, nextViewController) + let selectedDate = Utils.dateFormatterString(format: "yyyy.MM.dd", date: selectedDate ?? Date()) + coordinator?.showRecommendViewController(selectedDate: selectedDate) } func todayBtnTapped() { @@ -144,26 +154,15 @@ extension HomeViewController: HomeModalDelegate { func modifyMission(id: Int, type: MissionType) { - let updateMissionViewController = AddMissionViewController() - - updateMissionViewController.setMissionId(id) - updateMissionViewController.setViewType(type) - - Utils.push(self.navigationController, updateMissionViewController) + coordinator?.showModifyViewController(id: id, type: type) } func deleteMission(index: Int, id: Int) { - let modalViewController = HomeDeleteViewController() - - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.modalTransitionStyle = .crossDissolve - - modalViewController.deleteClosure = { + coordinator?.showDeleteViewController { [weak self] in + guard let self else { return } self.requestDeleteMission(index: index, id: id) } - - present(modalViewController, animated: false) } } @@ -173,28 +172,19 @@ extension HomeViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if !missionList.isEmpty { + let id = missionList[indexPath.item].id - let modalViewController = MissionDetailViewController() - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.userId = missionList[indexPath.item].id - - modalViewController.deleteClosure = { [weak self] in + coordinator?.showMissionDetailViewController(id: id, deleteClosure: { [weak self] in guard let self else { return } - self.dailyLoadData() self.weeklyLoadData() - self.missionDataSource.updateSnapShot(missionList: self.missionList) - } - - modalViewController.moveDateClosure = { [weak self] date in + self.missionDataSource.updateSnapShot(missionList: self.missionList ) + }, moveDateClosure: { [weak self] date in guard let self else { return } - let modifiedDate: Date = date.toDate(withFormat: "YYYY.MM.dd") self.weekCalendar.select(date: modifiedDate) self.requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: modifiedDate)) - } - - self.present(modalViewController, animated: true) + }) } } } @@ -236,8 +226,7 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - + guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } guard let percentage = getPercentage(for: date) else { return cell } cell.configure(percent: percentage) @@ -298,6 +287,7 @@ extension HomeViewController { situation: data.situationName, goal: "", action: [])) + coordinator?.dismiss() } } } @@ -355,12 +345,8 @@ extension HomeViewController { private func showPopup(isSelected: Bool) { if !(isSelected || didDeprecatedButtonTap) { - let nextView = CommonNotificationViewController() - nextView.modalPresentationStyle = .overFullScreen - nextView.modalTransitionStyle = .crossDissolve - self.present(nextView, animated: true) - - nextView.tapCloseButton = { + coordinator?.showPopupViewController { [weak self] in + guard let self else { return } self.didCloseButtonTap = true AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickAdModalClose(again: self.didDeprecatedButtonTap ? "yes": "no" )) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 5dce55d1..5e6baa7e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -29,21 +29,34 @@ final class MissionDetailViewController: UIViewController { var userId: Int? var deleteClosure: (() -> Void)? - var moveDateClosure: ((_ date: String) -> Void)? + var moveDateClosure: ((String) -> Void)? private lazy var safeArea = self.view.safeAreaLayoutGuide + private weak var coordinator: HomeCoordinator? + // MARK: - UI Components private let headerView = DetailHeaderView() private let deleteButton = UIButton(configuration: .filled()) private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + guard let id = self.userId else { return } requestDailyMissionAPI(id: id) } @@ -125,23 +138,19 @@ extension MissionDetailViewController { private func setupDataSource() { - let cellRegistration = CellRegistration {cell, _, item in + let cellRegistration = CellRegistration { cell, _, item in cell.configure(model: item) } - let footerRegistration = FooterRegistration(elementKind: UICollectionView.elementKindSectionFooter) { footerView, _, _ in - footerView.footerClosure = { - let modalViewController = DetailCalendarViewController() - modalViewController.detailModel = self.detailModel - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.modalTransitionStyle = .crossDissolve - guard let id = self.userId else {return} - modalViewController.userId = id - modalViewController.movedateClosure = { [weak self] date in - self?.dismiss(animated: true) - self?.moveDateClosure?(date) + let footerRegistration = FooterRegistration(elementKind: UICollectionView.elementKindSectionFooter) { [weak self] footerView, _, _ in + footerView.footerClosure = { [weak self] in + guard let self = self, + let id = self.userId, + let data = self.detailModel + else { return } + self.coordinator?.showSelectDateViewController(data: data, id: id) { date in + self.moveDateClosure?(date) } - self.present(modalViewController, animated: false) } } @@ -158,23 +167,18 @@ extension MissionDetailViewController { } private func configureHeaderView() { - headerView.cancelClosure = { + headerView.cancelClosure = { [weak self] in + guard let self else { return } self.view.alpha = 0 - self.dismiss(animated: true) { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) - } + coordinator?.dismiss() + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) } - headerView.editClosure = { + headerView.editClosure = { [weak self] in + guard let self else { return } + guard let id = self.userId else { return } + coordinator?.showModifyViewController(id: id, type: .update) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickEditMission(section: "detail")) - - let updateMissionViewController = AddMissionViewController() - guard let rootViewController = self.presentingViewController as? UINavigationController else { return } - updateMissionViewController.setMissionId(self.userId ?? 0) - updateMissionViewController.setViewType(.update) - self.dismiss(animated: true) { - rootViewController.pushViewController(updateMissionViewController, animated: true) - } } } @@ -207,21 +211,16 @@ extension MissionDetailViewController { func deleteBtnTapped() { guard let data = detailModel else { return } - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.clickDeleteMission(section: "detail", title: data.title, situation: data.situation, goal: data.goal, action: data.actions.map { $0.name })) - - let modalViewController = HomeDeleteViewController() - modalViewController.modalPresentationStyle = .overFullScreen - modalViewController.modalTransitionStyle = .crossDissolve - modalViewController.deleteClosure = { + coordinator?.showDeleteViewController { [weak self] in + guard let self else { return } guard let id = self.userId else { return } self.requestDeleteMission(id: id) } - - present(modalViewController, animated: false) } } @@ -257,10 +256,9 @@ extension MissionDetailViewController { goal: data.goal, action: data.actions.map { $0.name })) - self.dismiss(animated: true) { - - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) - } + coordinator?.dismiss() + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Detail.closeDetailMission) + } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift index dfe6d21a..a257c00a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift @@ -13,7 +13,7 @@ import SnapKit final class MyInfoViewController: UIViewController { // MARK: - Properties - + typealias CellRegistration = UICollectionView.CellRegistration typealias HeaderRegistration = UICollectionView.SupplementaryRegistration typealias DataSource = UICollectionViewDiffableDataSource @@ -27,6 +27,19 @@ final class MyInfoViewController: UIViewController { private lazy var safeArea = self.view.safeAreaLayoutGuide + private weak var coordinator: MypageCoordinator? + + // MARK: - init + + init(coordinator: MypageCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - UI Components private let myInfoCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) @@ -177,10 +190,7 @@ extension MyInfoViewController: UICollectionViewDelegate { private func profileSectionSelection() { sendAnalyticsEvent(.clickMyInfo) { - - let nextViewController = MyInfoAccountViewController() - nextViewController.hidesBottomBarWhenPushed = false - navigationController?.pushViewController(nextViewController, animated: true) + coordinator?.showMyInfoAccountViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift index 96176c3b..c67018d0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift @@ -11,6 +11,10 @@ import KakaoSDKUser final class MyInfoAccountViewController: UIViewController { + // MARK: - Property + + private weak var coordinator: MypageCoordinator? + // MARK: - UI Components private let navigationView = UIView() @@ -28,6 +32,19 @@ final class MyInfoAccountViewController: UIViewController { private let logoutButton = UIButton() private let withdrawButton = UIButton() + // MARK: - init + + init(coordinator: MypageCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.AccountInfo.viewAccountInfo) @@ -41,8 +58,8 @@ final class MyInfoAccountViewController: UIViewController { private extension MyInfoAccountViewController { func setUI() { - self.notificationView.switchClosure = { result in - self.notificationView.notificationSwitch.setOn(result, animated: true) + self.notificationView.switchClosure = { [weak self] result in + self?.notificationView.notificationSwitch.setOn(result, animated: true) } view.backgroundColor = .ntdBlack @@ -50,7 +67,7 @@ private extension MyInfoAccountViewController { backButton.do { $0.setBackgroundImage(.icBack, for: .normal) - $0.addTarget(self, action: #selector(self.popViewController), for: .touchUpInside) + $0.addTarget(self, action: #selector(popBackbutton), for: .touchUpInside) } navigationTitle.do { @@ -145,27 +162,19 @@ private extension MyInfoAccountViewController { @objc private func presentToWithdraw() { - let nextView = NottodoModalViewController() - nextView.modalPresentationStyle = .overFullScreen - nextView.modalTransitionStyle = .crossDissolve - nextView.pushToRootAction = { [weak self] in - self?.navigationController?.changeRootViewController(AuthViewController()) - } - self.present(nextView, animated: true) + coordinator?.showWithdrawViewController() } @objc private func tappedLogout() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.AccountInfo.appearLogoutModal) - - let logoutAlert = UIAlertController(title: I18N.logoutAlertTitle, message: I18N.logoutAlertmessage, preferredStyle: UIAlertController.Style.alert) - let logoutAction = UIAlertAction(title: I18N.logout, style: UIAlertAction.Style.default, handler: {_ in - self.logout() - }) - let cancelAlert = UIAlertAction(title: I18N.cancel, style: UIAlertAction.Style.default, handler: nil) - logoutAlert.addAction(cancelAlert) - logoutAlert.addAction(logoutAction) - present(logoutAlert, animated: true, completion: nil) - + coordinator?.showLogoutAlertController { [weak self] in + self?.logout() + } + } + + @objc + private func popBackbutton() { + coordinator?.popViewController() } } @@ -173,12 +182,13 @@ extension MyInfoAccountViewController { func logout() { if !KeychainUtil.getBool(DefaultKeys.isAppleLogin) { kakaoLogout() - } - AuthAPI.shared.deleteAuth { _ in - UserDefaults.standard.removeObject(forKey: DefaultKeys.accessToken) - UserDefaults.standard.removeObject(forKey: DefaultKeys.socialToken) + } + + AuthAPI.shared.deleteAuth { [weak self] _ in + guard let self else { return } + self.coordinator?.connectAuthCoordinator(type: .logout ) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.AccountInfo.completeLogout) - SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift index 9ba60cbc..6ebe5913 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift @@ -22,6 +22,7 @@ final class FifthOnboardingViewController: UIViewController { var fiveOnboardingModel: [FifthOnboardingModel] = FifthOnboardingModel.titles private var dataSource: UICollectionViewDiffableDataSource! = nil private lazy var safeArea = self.view.safeAreaLayoutGuide + private weak var coordinator: AuthCoordinator? // MARK: - UI Components @@ -30,6 +31,16 @@ final class FifthOnboardingViewController: UIViewController { private let arrowImage = UIImageView() private let gradientView = GradientView(color: .clear, color1: .ntdBlack!) + // MARK: - init + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -61,14 +72,16 @@ extension FifthOnboardingViewController { $0.isScrollEnabled = false } nextButton.do { - $0.configuration?.image = .kakaoAppleIcon - $0.configuration?.title = I18N.fifthButton - $0.configuration?.imagePadding = 7 - $0.configuration?.imagePlacement = NSDirectionalRectEdge.leading - $0.configuration?.attributedTitle?.font = .Pretendard(.medium, size: 16) - $0.configuration?.baseForegroundColor = .white - $0.configuration?.contentInsets = NSDirectionalEdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 10) - $0.addTarget(self, action: #selector(ButtonTapped), for: .touchUpInside) + var configuration = UIButton.Configuration.plain() + configuration.image = .kakaoAppleIcon + configuration.title = I18N.fifthButton + configuration.imagePadding = 7 + configuration.imagePlacement = NSDirectionalRectEdge.leading + configuration.attributedTitle?.font = .Pretendard(.medium, size: 16) + configuration.baseForegroundColor = .white + configuration.contentInsets = NSDirectionalEdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 10) + $0.configuration = configuration + $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } arrowImage.do { $0.image = .splashBack @@ -144,15 +157,15 @@ extension FifthOnboardingViewController { let section = self.dataSource.snapshot().sectionIdentifiers[sectionIndex] switch section { case .main: - return self.MainSection() + return self.mainSection() default: - return self.SubSection() + return self.subSection() } }) return layout } - private func MainSection() -> NSCollectionLayoutSection { + private func mainSection() -> NSCollectionLayoutSection { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(70))) let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(70)), subitems: [item]) let section = NSCollectionLayoutSection(group: group) @@ -166,7 +179,7 @@ extension FifthOnboardingViewController { return section } - private func SubSection() -> NSCollectionLayoutSection { + private func subSection() -> NSCollectionLayoutSection { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))) let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(70)), subitems: [item]) let section = NSCollectionLayoutSection(group: group) @@ -178,12 +191,9 @@ extension FifthOnboardingViewController { extension FifthOnboardingViewController { @objc - private func ButtonTapped() { + private func buttonTapped() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext5) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - UIView.animate(withDuration: 0.01) { - SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) - } - } + + self.coordinator?.showSignUpViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FourthOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FourthOnboardingViewController.swift index f38c3c7a..5da1868f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FourthOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FourthOnboardingViewController.swift @@ -20,6 +20,7 @@ final class FourthOnboardingViewController: UIViewController { private let onboardingModel: [FourthOnboardingModel] = FourthOnboardingModel.items private var dataSource: UICollectionViewDiffableDataSource! = nil private lazy var safeArea = self.view.safeAreaLayoutGuide + private weak var coordinator: AuthCoordinator? // MARK: - UI Components @@ -27,6 +28,17 @@ final class FourthOnboardingViewController: UIViewController { private let nextButton = UIButton(configuration: .plain()) private let gradientView = GradientView(color: .clear, color1: .ntdBlack!) + // MARK: - init + + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -63,9 +75,9 @@ extension FourthOnboardingViewController { $0.configuration?.attributedTitle?.font = .Pretendard(.medium, size: 16) $0.configuration?.baseForegroundColor = .white $0.configuration?.contentInsets = NSDirectionalEdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0) - $0.addTarget(self, action: #selector(ButtonTapped), for: .touchUpInside) + $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } - + } private func setLayout() { @@ -128,14 +140,9 @@ extension FourthOnboardingViewController { } extension FourthOnboardingViewController { @objc - private func ButtonTapped() { + private func buttonTapped() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext4) - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - UIView.animate(withDuration: 0.01) { - let nextViewController = FifthOnboardingViewController() - self.navigationController?.pushViewController(nextViewController, animated: false) - } - } + + self.coordinator?.showFifthOnboardingViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift index 0e9391fd..ac7e72e4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift @@ -9,16 +9,29 @@ import UIKit import AVFoundation final class LogoOnboardingViewController: UIViewController { - + // MARK: - Properties private lazy var safeArea = self.view.safeAreaLayoutGuide - + private weak var coordinator: AuthCoordinator? + private var player: AVPlayer? + // MARK: - UI Components private let animationView = UIImageView() private let nextButton = UIButton() + // MARK: - init + + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -31,12 +44,18 @@ final class LogoOnboardingViewController: UIViewController { super.viewWillAppear(animated) playMp4Video() } + + deinit { + NotificationCenter.default.removeObserver(self) + player?.pause() // 메모리 해제 전에 player 정지 + player = nil + } } // MARK: - Methods extension LogoOnboardingViewController { - + private func playMp4Video() { DispatchQueue.main.async { [weak self] in self?.playVideo(with: "logo") @@ -44,16 +63,20 @@ extension LogoOnboardingViewController { } private func playVideo(with resourceName: String) { - guard let path = Bundle.main.path(forResource: resourceName, ofType: "mp4") else { - return - } + guard let path = Bundle.main.path(forResource: resourceName, ofType: "mp4") + else { return } + let player = AVPlayer(url: URL(fileURLWithPath: path)) + self.player = player + let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = animationView.bounds animationView.layer.addSublayer(playerLayer) playerLayer.videoGravity = .resizeAspectFill - NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem) + NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), + name: .AVPlayerItemDidPlayToEndTime, + object: player.currentItem) player.play() } @@ -86,15 +109,14 @@ extension LogoOnboardingViewController { } } - @objc private func videoDidFinishPlaying(notification: NSNotification) { + @objc + private func videoDidFinishPlaying(notification: NSNotification) { nextButton.isHidden = false } - + @objc private func buttonTapped() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingStart) - - let nextViewController = SecondOnboardingViewController() - navigationController?.pushViewController(nextViewController, animated: false) + coordinator?.showSecondOnboardingViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/OnboardingHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/OnboardingHeaderView.swift index 1ba53cb6..ea0061e8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/OnboardingHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/OnboardingHeaderView.swift @@ -108,7 +108,7 @@ extension OnboardingHeaderView { horizontalStackView.snp.makeConstraints { $0.bottom.equalTo(titleLabel.snp.top).inset(20) } - + flagImage.snp.makeConstraints { $0.size.equalTo(20) } @@ -128,6 +128,7 @@ extension OnboardingHeaderView { self.titleLabel.text = title self.subTitleLabel.text = subTitle } + func configure(isControl: Bool, title: String, subTitle: String) { self.isControl = isControl updateUI(title: title, subTitle: subTitle) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/SecondOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/SecondOnboardingViewController.swift index 67923f13..5cbb9d09 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/SecondOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/SecondOnboardingViewController.swift @@ -20,11 +20,23 @@ final class SecondOnboardingViewController: UIViewController { private let onboardingModel: [SecondOnboardingModel] = SecondOnboardingModel.titles private var dataSource: UICollectionViewDiffableDataSource! = nil private lazy var safeArea = self.view.safeAreaLayoutGuide - + private weak var coordinator: AuthCoordinator? + // MARK: - UI Components private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + // MARK: - init + + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -106,12 +118,7 @@ extension SecondOnboardingViewController { extension SecondOnboardingViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext2(select: SecondOnboardingModel.titles[indexPath.row].title)) - - let destinationViewController = ThirdOnboardingViewController() - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - UIView.animate(withDuration: 0.01) { - self.navigationController?.pushViewController(destinationViewController, animated: false) - } - } + + coordinator?.showThirdOnboardingViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift index 01b7ce18..b798cca5 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift @@ -22,13 +22,24 @@ final class ThirdOnboardingViewController: UIViewController { private var dataSource: UICollectionViewDiffableDataSource! = nil private lazy var safeArea = self.view.safeAreaLayoutGuide private var selectList: [String] = [] - + private weak var coordinator: AuthCoordinator? + // MARK: - UI Components private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) private let nextButton = UIButton() private var isTapped: Bool = false + // MARK: - init + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -61,7 +72,7 @@ extension ThirdOnboardingViewController { } nextButton.do { $0.backgroundColor = isTapped ? .white : .gray2 - $0.isUserInteractionEnabled = isTapped ? true : false + $0.isUserInteractionEnabled = isTapped $0.layer.cornerRadius = 25 $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(isTapped ? .black :.gray4, for: .normal) @@ -73,14 +84,14 @@ extension ThirdOnboardingViewController { private func updateButton(isTapped: Bool) { nextButton.do { $0.backgroundColor = isTapped ? .white : .gray2 - $0.isUserInteractionEnabled = isTapped ? true : false + $0.isUserInteractionEnabled = isTapped $0.setTitleColor(isTapped ? .black :.gray4, for: .normal) } } private func setLayout() { view.addSubviews(collectionView, nextButton) - + nextButton.snp.makeConstraints { $0.top.equalTo(collectionView.snp.bottom) $0.bottom.equalTo(safeArea).inset(10) @@ -135,14 +146,9 @@ extension ThirdOnboardingViewController { extension ThirdOnboardingViewController { @objc private func buttonTapped() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - UIView.animate(withDuration: 0.01) { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext3(select: self.selectList)) - - let nextViewController = FourthOnboardingViewController() - self.navigationController?.pushViewController(nextViewController, animated: false) - } - } + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext3(select: self.selectList)) + self.coordinator?.showFourthOnboardingViewController() } } @@ -165,7 +171,7 @@ extension ThirdOnboardingViewController: UICollectionViewDelegate { } self.isTapped = false updateButton(isTapped: self.isTapped) - + } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ValueOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ValueOnboardingViewController.swift index c61abe83..36f08add 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ValueOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ValueOnboardingViewController.swift @@ -13,8 +13,23 @@ final class ValueOnboardingViewController: UIViewController { // MARK: - Properties + private weak var coordinator: AuthCoordinator? + + // MARK: - UI Properties + let animationView = LottieAnimationView() + // MARK: - init + + init(coordinator: AuthCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewDidLoad() { @@ -23,6 +38,11 @@ final class ValueOnboardingViewController: UIViewController { self?.pushToNextViewController() } } + + deinit { + animationView.stop() + animationView.removeFromSuperview() + } } // MARK: - Methods @@ -39,13 +59,13 @@ extension ValueOnboardingViewController { animationView.frame = view.bounds view.addSubview(animationView) - animationView.play { _ in + animationView.play { [weak self] _ in + self?.animationView.removeFromSuperview() // 애니메이션 뷰 제거 completion() } } func pushToNextViewController() { - let nextViewController = LogoOnboardingViewController() - navigationController?.pushViewController(nextViewController, animated: false) + coordinator?.showLogoOnboardingViewController() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift index 3f2594ae..e2128a61 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift @@ -15,14 +15,14 @@ final class RecommendCollectionViewCell: UICollectionViewCell { // MARK: - Properties static let identifier = "RecommendCollectionViewCell" - var id: Int = 0 + private var id: Int = 0 // MARK: - UI Components - let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 17, bottom: 4, right: 17)) + private let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 17, bottom: 4, right: 17)) private let titleLabel = UILabel() private let bodyLabel = UILabel() - let bodyImage = UIImageView() + private let bodyImage = UIImageView() // MARK: - View Life Cycle @@ -36,7 +36,6 @@ final class RecommendCollectionViewCell: UICollectionViewCell { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - } // MARK: - Methods @@ -68,7 +67,6 @@ extension RecommendCollectionViewCell { $0.textColor = .gray4 $0.numberOfLines = 0 } - } private func setLayout() { @@ -104,5 +102,4 @@ extension RecommendCollectionViewCell { bodyLabel.text = model.description bodyImage.setImage(with: model.image) } - } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift index e9e3d3e6..32abdf86 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift @@ -14,7 +14,9 @@ final class RecommendViewController: UIViewController { var recommendResponse: RecommendResponseDTO? var recommendList: [RecommendResponseDTO] = [] private var selectDay: String? - + + private weak var coordinator: HomeCoordinator? + // MARK: - UI Components private let navigationView = UIView() @@ -27,6 +29,17 @@ final class RecommendViewController: UIViewController { private let recommendInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) private let cellHeight: CGFloat = 137 + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - View Life Cycle override func viewDidLoad() { @@ -62,7 +75,7 @@ private extension RecommendViewController { dismissButton.do { $0.setBackgroundImage(.delete, for: .normal) - $0.addTarget(self, action: #selector(self.popViewController), for: .touchUpInside) + $0.addTarget(self, action: #selector(dismissViewController), for: .touchUpInside) } navigationTitle.do { @@ -111,7 +124,7 @@ private extension RecommendViewController { $0.leading.trailing.equalToSuperview().inset(15) $0.bottom.equalToSuperview().offset(-45) } - + recommendCollectionView.snp.makeConstraints { $0.top.equalTo(seperateView.snp.bottom).offset(16) $0.leading.trailing.equalToSuperview() @@ -127,7 +140,7 @@ private extension RecommendViewController { private func register() { recommendCollectionView.register(RecommendCollectionViewCell.self, - forCellWithReuseIdentifier: RecommendCollectionViewCell.identifier) + forCellWithReuseIdentifier: RecommendCollectionViewCell.identifier) } func setDelegate() { @@ -137,22 +150,20 @@ private extension RecommendViewController { func requestRecommendAPI() { RecommendAPI.shared.getRecommend { [weak self] response in - guard self != nil else { return } + guard let self else { return } guard let response = response else { return } guard let data = response.data else { return } - self?.recommendList = data - self?.recommendCollectionView.reloadData() + self.recommendList = data + self.recommendCollectionView.reloadData() } } @objc private func buttonTapped() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Recommend.clickSelfCreateMission) - - let nextViewController = AddMissionViewController() - nextViewController.setDate([selectDay ?? ""]) - navigationController?.pushViewController(nextViewController, animated: true) + let data: AddMissionData = AddMissionData(date: [selectDay ?? ""]) + coordinator?.showAddViewController(data: data, type: .add) } } @@ -188,15 +199,20 @@ extension RecommendViewController: UICollectionViewDelegate { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Recommend.clickRecommendMission(situation: recommendList[indexPath.row].situation, title: recommendList[indexPath.row].title)) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { - let selectedCell = collectionView.cellForItem(at: indexPath) as! RecommendCollectionViewCell - let viewController = RecommendActionViewController() - - viewController.selectedIndex = selectedCell.id - viewController.tagLabelText = selectedCell.tagLabel.text - viewController.bodyImageUrl = selectedCell.bodyImage.image - viewController.setSelectDate(self.selectDay ?? "") - - self.navigationController?.pushViewController(viewController, animated: true) + let indexPath = self.recommendList[indexPath.item] + let data = RecommendActionData(tag: indexPath.situation, + image: indexPath.image, + index: indexPath.id, + selectedDate: self.selectDay) + self.coordinator?.showRecommendDetailViewController(actionData: data) } } } + +extension RecommendViewController { + + @objc + private func dismissViewController() { + coordinator?.dismissRecommendViewcontroller() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift index 2b777ea6..3eb3684d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift @@ -18,7 +18,7 @@ final class RecommendActionCollectionViewCell: UICollectionViewCell { override var isSelected: Bool { didSet { contentView.layer.borderColor = isSelected ? UIColor.gray3?.cgColor : UIColor.clear.cgColor - checkIcon.isHidden = isSelected ? false : true + checkIcon.isHidden = !isSelected } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift index d9b31489..79d660a6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift @@ -129,10 +129,10 @@ extension RecommendActionHeaderView { $0.leading.equalTo(recommendLabel.snp.leading) } } - - func configure(tag: String?, title: String?, image: UIImage?) { - self.tagLabel.text = tag - self.bodyImage.image = image + + func configure(data: RecommendActionData, title: String?) { + self.tagLabel.text = data.tag + self.bodyImage.setImage(with: data.image) self.titleLabel.text = title } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/ViewControllers/RecommendActionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/ViewControllers/RecommendActionViewController.swift index 0913b3d0..42984144 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/ViewControllers/RecommendActionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/ViewControllers/RecommendActionViewController.swift @@ -10,20 +10,36 @@ import UIKit import SnapKit import Then +struct RecommendActionData { + let tag: String + let image: String + let index: Int + let selectedDate: String? +} + +struct AddMissionData { + var nottodo: String? + var action: String? + var situation: String? + var date: [String] + + init(nottodo: String? = nil, action: String? = nil, situation: String? = nil, date: [String] = []) { + self.nottodo = nottodo + self.action = action + self.situation = situation + self.date = date + } +} + final class RecommendActionViewController: UIViewController { // MARK: - Properties - private var recommendActionResponse: RecommendActionResponseDTO? private var recommendActionList: [RecommendActions] = [] - var recommendList: [RecommendResponseDTO] = [] - var selectedIndex: Int = 0 - var tagLabelText: String? - var bodyImageUrl: UIImage? - var nottodoTitle: String? - var actionLabel: String? - var situationLabel: String? - private var selectDay: String? + private var addActionData = AddMissionData() + var actionHeaderData: RecommendActionData? + + private weak var coordinator: HomeCoordinator? // MARK: - UI Components @@ -31,16 +47,31 @@ final class RecommendActionViewController: UIViewController { private let backButton = UIButton() private let navigationTitle = UILabel() private let nextButton = UIButton() - private var isTapped: Bool = false + private var isTapped: Bool = false { + didSet { + setUI() + } + } private lazy var recommendActionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) private let recommendActionInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) + // MARK: - init + + init(coordinator: HomeCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - View Life Cycle override func viewDidLoad() { super.viewDidLoad() - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.RecommendDetail.viewRecommendMissionDetail(situation: situationLabel ?? "", title: nottodoTitle ?? "")) + setUI() setLayout() register() @@ -49,11 +80,12 @@ final class RecommendActionViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - requestRecommendActionAPI() - } - - func setSelectDate(_ date: String) { - selectDay = date + if let index = actionHeaderData?.index { + requestRecommendActionAPI(index: index) + } + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.RecommendDetail.viewRecommendMissionDetail(situation: addActionData.situation ?? "", + title: addActionData.nottodo ?? "")) } } @@ -62,12 +94,7 @@ final class RecommendActionViewController: UIViewController { extension RecommendActionViewController { @objc private func pushToAddMission() { - let nextViewController = AddMissionViewController() - nextViewController.setNottodoLabel(nottodoTitle ?? "") - nextViewController.setActionLabel(actionLabel ?? "") - nextViewController.setSituationLabel(situationLabel ?? "") - nextViewController.setDate([selectDay ?? ""]) - navigationController?.pushViewController(nextViewController, animated: true) + coordinator?.showAddViewController(data: self.addActionData, type: .add) } } @@ -96,8 +123,8 @@ private extension RecommendActionViewController { } nextButton.do { - $0.isHidden = isTapped ? false : true - $0.isUserInteractionEnabled = isTapped ? true : false + $0.isHidden = !isTapped + $0.isUserInteractionEnabled = isTapped $0.setTitle(I18N.next, for: .normal) $0.setTitleColor(.gray1, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 17.18) // 수정 필요 @@ -160,20 +187,9 @@ private extension RecommendActionViewController { recommendActionCollectionView.dataSource = self } - func requestRecommendActionAPI() { - RecommendAPI.shared.getRecommendAction(index: selectedIndex) { [weak self] response in - guard self != nil else { return } - guard let response = response else { return } - guard let data = response.data else { return } - - self?.recommendActionList = data.recommendActions - self?.recommendActionCollectionView.reloadData() - } - } - @objc func backButtonDidTapped() { - self.navigationController?.popViewController(animated: true) + coordinator?.popViewController() } } @@ -222,60 +238,64 @@ extension RecommendActionViewController: UICollectionViewDataSource { guard let cell = collectionView.dequeueReusableCell( withReuseIdentifier: RecommendActionCollectionViewCell.identifier, for: indexPath) as? RecommendActionCollectionViewCell else { return UICollectionViewCell() } + cell.configure(model: recommendActionList[indexPath.row]) + return cell } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - if kind == UICollectionView.elementKindSectionHeader { - guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: RecommendActionHeaderView.identifier, for: indexPath) as? RecommendActionHeaderView else { return UICollectionReusableView() } - RecommendAPI.shared.getRecommendAction(index: selectedIndex) { [weak self] response in - guard self != nil else { return } - guard let response = response else { return } - guard let data = response.data else { return } - headerView.configure(tag: self?.tagLabelText, title: data.title, image: self?.bodyImageUrl) - self?.nottodoTitle = headerView.getTitle() - self?.situationLabel = headerView.getSituation() - } + switch kind { + case UICollectionView.elementKindSectionHeader: + guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: RecommendActionHeaderView.identifier, for: indexPath) as? RecommendActionHeaderView + else { return UICollectionReusableView() } + + headerView.configure(data: self.actionHeaderData!, title: self.addActionData.nottodo ) return headerView - } else { - guard let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: RecommendActionFooterView.identifier, for: indexPath) as? RecommendActionFooterView else { return UICollectionReusableView() } + case UICollectionView.elementKindSectionFooter: + guard let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: RecommendActionFooterView.identifier, for: indexPath) as? RecommendActionFooterView + else { return UICollectionReusableView() } + footerView.clickedNextButton = { [weak self] in - let nextViewContoller = AddMissionViewController() - nextViewContoller.setNottodoLabel(self?.nottodoTitle ?? "") - nextViewContoller.setSituationLabel(self?.situationLabel ?? "") - nextViewContoller.setDate([self?.selectDay ?? ""]) - self?.navigationController?.pushViewController(nextViewContoller, animated: true) + guard let self else { return } + self.addActionData.action = "" + coordinator?.showAddViewController(data: self.addActionData, type: .add) } + return footerView + default: + return UICollectionReusableView() } } + } extension RecommendActionViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.RecommendDetail.clickCreateRecommendMission(action: recommendActionList[indexPath.row].name, situation: situationLabel ?? "", title: nottodoTitle ?? "")) + self.isTapped = true + addActionData.action = recommendActionList[indexPath.item].name + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.RecommendDetail.clickCreateRecommendMission(action: recommendActionList[indexPath.row].name, + situation: addActionData.situation ?? "", + title: addActionData.nottodo ?? "")) - if let select = collectionView.indexPathsForSelectedItems { - if select.count > 0 { - self.isTapped = true - setUI() - setDelegate() - } - } - guard let cell = collectionView.cellForItem(at: indexPath) as? RecommendActionCollectionViewCell else { fatalError() } - actionLabel = cell.titleLabel.text } +} + +extension RecommendActionViewController { - func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { - if let deSelect = collectionView.indexPathsForSelectedItems { - if deSelect.count == 0 { - self.isTapped = false - setUI() - setDelegate() - } + func requestRecommendActionAPI(index: Int) { + RecommendAPI.shared.getRecommendAction(index: index) { [weak self] response in + guard let self = self, + let response = response, + let data = response.data, + let selectedDate = self.actionHeaderData?.selectedDate, + let situation = self.actionHeaderData?.tag + else { return } + + self.recommendActionList = data.recommendActions + self.addActionData = AddMissionData(nottodo: data.title, situation: situation, date: [selectedDate]) + self.recommendActionCollectionView.reloadData() } - actionLabel = "" } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarController.swift index 87a0cba0..0ec77904 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarController.swift @@ -13,12 +13,12 @@ final class TabBarController: UITabBarController { private var tabs: [UIViewController] = [] private lazy var defaultTabBarHeight = { tabBar.frame.size.height }() - + // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() - setTabBarItems() + setTabBarUI() } @@ -28,25 +28,19 @@ final class TabBarController: UITabBarController { } } -private extension TabBarController { - func setTabBarItems() { - tabs = [HomeViewController(), - AchievementViewController(), - MyInfoViewController() - ] - - TabBarItemType.allCases.forEach { - tabs[$0.rawValue].tabBarItem = $0.setTabBarItem() - tabs[$0.rawValue].tabBarItem.tag = $0.rawValue - tabs[$0.rawValue].tabBarItem.imageInsets = UIEdgeInsets.init( - top: convertByHeightRatio(16), - left: 0, - bottom: convertByHeightRatio(-16), - right: 0 - ) +extension TabBarController { + + func setTabBarItems() -> [UITabBarItem] { + let tabBarItems = TabBarItemType.allCases.map { + let tabBarItem = $0.setTabBarItem() + tabBarItem.tag = $0.rawValue + tabBarItem.imageInsets = UIEdgeInsets(top: convertByHeightRatio(16), + left: 0, + bottom: convertByHeightRatio(-16), + right: 0) + return tabBarItem } - - setViewControllers(tabs, animated: false) + return tabBarItems } func setTabBarUI() { @@ -63,7 +57,7 @@ private extension TabBarController { var newFrame = tabBar.frame newFrame.size.height = newTabBarHeight newFrame.origin.y = view.frame.size.height - newTabBarHeight - + tabBar.frame = newFrame } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarItem.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarItem.swift index 217e3f22..13f27031 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarItem.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/TabBar/TabBarItem.swift @@ -41,6 +41,6 @@ extension TabBarItemType { title: nil, image: unselectedIcon.withRenderingMode(.alwaysOriginal), selectedImage: selectedIcon.withRenderingMode(.alwaysOriginal) - ) + ) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift index 904585ef..12bbde9b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift @@ -9,6 +9,8 @@ import UIKit final class UpdateCheckViewController: UIViewController { + // MARK: - Properties + enum UpdateType { case optional case force(newVersion: String) @@ -23,6 +25,21 @@ final class UpdateCheckViewController: UIViewController { static let appstoreURL: String = "itms-apps://itunes.apple.com/app/\(Bundle.main.appleId)" } + private weak var coordinator: UpdateCoordinator? + + // MARK: - init + + init(coordinator: UpdateCoordinator) { + self.coordinator = coordinator + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() @@ -42,26 +59,18 @@ extension UpdateCheckViewController { let updateType = try await checkUpdateType() switch updateType { case .force(let newVersion): - self.showForceUpdateAlert(newVersion: newVersion) + coordinator?.showForceUpdateAlertController(newVersion: newVersion) case .optional: - self.showUpdateAlert() + coordinator?.showUpdateAlertController() case .none: - self.changeMainViewController() + coordinator?.changeMainViewController() } } catch { - self.changeMainViewController() + coordinator?.changeMainViewController() } } } - private func changeMainViewController() { - if KeychainUtil.getAccessToken().isEmpty { - SceneDelegate.shared?.changeRootViewControllerTo(ValueOnboardingViewController()) - } else { - SceneDelegate.shared?.changeRootViewControllerTo(TabBarController()) - } - } - private func checkUpdateType() async throws -> UpdateType { // 앱스토어 버전 guard let appstoreVersion = try await getAppstoreVersion() else { @@ -127,54 +136,4 @@ extension UpdateCheckViewController { } return appStoreVersion } - - /// 선택 업데이트 경고창 - private func showUpdateAlert() { - let alertController = UIAlertController( - title: I18N.update, - message: I18N.updateAlert, - preferredStyle: .alert - ) - - let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in - // App Store로 이동 - if let appStoreURL = URL(string: Constant.appstoreURL) { - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: { [weak self] _ in - self?.changeMainViewController() - }) - } - } - - let cancelAction = UIAlertAction(title: I18N.later, style: .default) { _ in - // App Store로 이동 - if let appStoreURL = URL(string: Constant.appstoreURL) { - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: { [weak self] _ in - self?.changeMainViewController() - }) - } - } - - alertController.addAction(updateAction) - alertController.addAction(cancelAction) - - self.present(alertController, animated: true) - } - - /// 강제 업데이트 경고창 - private func showForceUpdateAlert(newVersion: String) { - let alertController = UIAlertController( - title: I18N.update, - message: I18N.forceUpdateAlert(newVersion: newVersion), - preferredStyle: .alert - ) - - let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in - // App Store로 이동 - if let appStoreURL = URL(string: Constant.appstoreURL) { - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) - } - } - alertController.addAction(updateAction) - self.present(alertController, animated: true) - } } From 5d8a61447dc1616ad4e586f7b1b47e2e6f075b87 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:47:45 +0900 Subject: [PATCH 05/21] =?UTF-8?q?[Feat]=20#241=20-=20network=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Network/API/BaseAPI.swift | 120 +++++++++++------- .../Network/API/Mission/MissionAPI.swift | 23 +++- .../iOS-NOTTODO/Network/Base/APIError.swift | 20 +++ .../Network/Base/ErrorReponse.swift | 13 ++ .../Network/Base/NetworkBase.swift | 30 ----- .../Network/Service/BaseService.swift | 69 ++++++++++ 6 files changed, 194 insertions(+), 81 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift delete mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift index 2e717c66..35cfe552 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift @@ -6,64 +6,90 @@ // import Foundation +import Combine +import Alamofire import Moya -enum BaseDomain { - case auth - case mission - case recommend +final class BaseAPI { + + typealias API = Target + + // MARK: - Properties + + var cancelBag = Set() + + lazy var provider = self.defaultProvider + + private lazy var defaultProvider: MoyaProvider = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10 + configuration.timeoutIntervalForResource = 10 + configuration.requestCachePolicy = .reloadIgnoringLocalCacheData + let interceptor = AuthInterceptor.shared + let session = Session(configuration: configuration, interceptor: interceptor) + let provider = MoyaProvider( + session: session, + plugins: [MoyaLoggingPlugin()] + ) + return provider + }() + + // MARK: - Initializers + + public init() {} } -extension BaseDomain { - - var url: String { - switch self { - case .auth: - return "/auth" - case .mission: - return "/mission" - case .recommend: - return "/recommend" - } +// MARK: - Providers + +extension BaseAPI { + var `default`: BaseAPI { + self.provider = self.defaultProvider + return self } } -protocol BaseService: TargetType { - var domain: BaseDomain { get } - var urlPath: String { get } - var headerType: HeaderType { get } -} +// MARK: - MakeRequests -extension BaseService { - var baseURL: URL { - return URL(string: Bundle.main.baseURL)! - } - - var path: String { - return domain.url + urlPath - } +extension BaseAPI { - var validationType: ValidationType { - return .successCodes + func requestWithCombine(_ target: API) -> AnyPublisher { + return Future { promise in + self.provider.request(target) { response in + switch response { + case .success(let value): + do { + let decoder = JSONDecoder() + let body = try decoder.decode(T.self, from: value.data) + promise(.success(body)) + } catch let error { + promise(.failure(error)) + } + case .failure(let error): + if case MoyaError.underlying(let error, _) = error, + case AFError.requestRetryFailed(let retryError, _) = error, + let retryError = retryError as? APIError, + retryError == APIError.tokenReissuanceFailed { + promise(.failure(retryError)) + } else { + promise(.failure(error)) + } + } + } + }.eraseToAnyPublisher() } - var headers: [String: String]? { - return headerType.value - } -} - -public enum HeaderType { - case json - case jsonWithToken - - public var value: [String: String] { - switch self { - case .json: - return ["Content-Type": "application/json"] - case .jsonWithToken: - return ["Content-Type": "application/json", - "Authorization": "\(KeychainUtil.getAccessToken())"] - } + // status codea만 사용하는 경우 + func requestWithCombineNoResult(_ target: API) -> AnyPublisher { + return Future { promise in + self.provider.request(target) { response in + switch response { + case .success(let value): + promise(.success(value.statusCode)) + case .failure(let error): + promise(.failure(error)) + } + } + }.eraseToAnyPublisher() } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift index 20d414c0..09f8a39f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift @@ -6,15 +6,30 @@ // import Foundation +import Combine import Moya +typealias DefaultMissionAPI = BaseAPI + +// 전체 수정 후 - 네이밍 변경 MissionAPI +protocol MissionAPIProtocol { + func getDailyMission(date: String) -> AnyPublisher +} + +extension DefaultMissionAPI: MissionAPIProtocol { + + func getDailyMission(date: String) -> AnyPublisher { + return requestWithCombine(MissionService.dailyMission(date: date)) + } +} + typealias DailyMissionData = GeneralArrayResponse typealias DetailMissionData = GeneralResponse typealias CalendarData = GeneralArrayResponse typealias RecentMissionData = GeneralArrayResponse typealias UpdateMissionData = GeneralResponse -typealias AddMissionData = GeneralResponse +typealias AddMissionsData = GeneralResponse typealias AddAnotherDay = GeneralResponse typealias UpdateMissionStatus = GeneralResponse @@ -28,7 +43,7 @@ protocol MissionAPIType { func deleteMission(id: Int, completion: @escaping (GeneralResponse?) -> Void) func patchUpdateMissionStatus(id: Int, status: String, completion: @escaping (UpdateMissionStatus?) -> Void) func postAnotherDay(id: Int, dates: [String], completion: @escaping (AddAnotherDay?) -> Void) - func postAddMission(request: AddMissionRequest, completion: @escaping(AddMissionData?) -> Void) + func postAddMission(request: AddMissionRequest, completion: @escaping(AddMissionsData?) -> Void) func putUpdateMission(request: UpdateMissionRequest, completion: @escaping(UpdateMissionData?) -> Void) } @@ -200,12 +215,12 @@ final class MissionAPI: MissionAPIType { } func postAddMission(request: AddMissionRequest, - completion: @escaping(AddMissionData?) -> Void) { + completion: @escaping(AddMissionsData?) -> Void) { provider.request(.addMission(request: request)) { result in switch result { case .success(let response): do { - let response = try response.map(AddMissionData?.self) + let response = try response.map(AddMissionsData?.self) completion(response) } catch let err { print(err.localizedDescription, 500) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift new file mode 100644 index 00000000..e260a0c4 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift @@ -0,0 +1,20 @@ +// +// APIError.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +public enum APIError: Error, Equatable { + case network(statusCode: Int, response: ErrorResponse) + case unknown + case tokenReissuanceFailed + + init(error: Error, statusCode: Int? = 0, response: ErrorResponse) { + guard let statusCode else { self = .unknown ; return } + + self = .network(statusCode: statusCode, response: response) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift new file mode 100644 index 00000000..3aa21914 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift @@ -0,0 +1,13 @@ +// +// ErrorReponse.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +public struct ErrorResponse: Decodable, Equatable { + public let statusCode: String + public let responseMessage: String +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift deleted file mode 100644 index e86fbf59..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NetworkBaes.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -struct NetworkBase { - - static func judgeStatus(by statusCode: Int, _ data: Data, _ t: T.Type) -> NetworkResult { - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GeneralResponse.self, from: data) - else { return .pathErr } - print(decodedData) - switch statusCode { - case 200: - return .success(decodedData.data as Any) - case 201..<300: - return .success(decodedData.status) - case 400..<500: - return .requestErr(decodedData.status) - case 500: - return .serverErr - default: - return .networkFail - } - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift new file mode 100644 index 00000000..479c804c --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift @@ -0,0 +1,69 @@ +// +// BaseService.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +import Moya + +enum BaseDomain { + case auth + case mission + case recommend +} + +extension BaseDomain { + + var url: String { + switch self { + case .auth: + return "/auth" + case .mission: + return "/mission" + case .recommend: + return "/recommend" + } + } +} + +protocol BaseService: TargetType { + var domain: BaseDomain { get } + var urlPath: String { get } + var headerType: HeaderType { get } +} + +extension BaseService { + var baseURL: URL { + return URL(string: Bundle.main.baseURL)! + } + + var path: String { + return domain.url + urlPath + } + + var validationType: ValidationType { + return .successCodes + } + + var headers: [String: String]? { + return headerType.value + } +} + +public enum HeaderType { + case json + case jsonWithToken + + public var value: [String: String] { + switch self { + case .json: + return ["Content-Type": "application/json"] + case .jsonWithToken: + return ["Content-Type": "application/json", + "Authorization": "\(KeychainUtil.getAccessToken())"] + } + } +} From b019368a950f6a58d679182b5844ce8ff945f76f Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:47:58 +0900 Subject: [PATCH 06/21] =?UTF-8?q?[Feat]=20#241=20-=20viewmodel=20Protocol?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Global/Protocol/ViewModel.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift new file mode 100644 index 00000000..ad0a11c6 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift @@ -0,0 +1,15 @@ +// +// ViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +protocol ViewModel where Self: AnyObject { + associatedtype Input + associatedtype Output + + func transform(input: Input) -> Output +} From 9b00090fcf206b57062f6736434d19c35104bf49 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:48:40 +0900 Subject: [PATCH 07/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20=EB=94=94=ED=85=8C=EC=9D=BC=20Manager=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/Manager/AchieveManagerImpl.swift | 27 +++++++++++++++++++ .../ManagerInterface/AchieveManager.swift | 13 +++++++++ 2 files changed, 40 insertions(+) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift new file mode 100644 index 00000000..3fe3a19c --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -0,0 +1,27 @@ +// +// AchieveManagerImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class AchieveManagerImpl: AchieveManager { + + private let missionAPI: DefaultMissionAPI + private let cancelBag = Set() + + init(missionAPI: DefaultMissionAPI) { + self.missionAPI = missionAPI + } + + func getDailyMission(date: String) -> AnyPublisher { + missionAPI.getDailyMission(date: date) + .map { data -> AchieveDetailData in + return AchieveDetailData(missionList: data.data ?? [], selectedDate: date) + } + .eraseToAnyPublisher() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift new file mode 100644 index 00000000..fdbb718c --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift @@ -0,0 +1,13 @@ +// +// AchieveManager.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol AchieveManager { + func getDailyMission(date: String) -> AnyPublisher +} From a0e1745bd35a7e88725ae70bd680f5fc271d5e5f Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:49:06 +0900 Subject: [PATCH 08/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20=EB=94=94=ED=85=8C=EC=9D=BC=20ViewModel=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/DetailAchievementModel.swift | 24 ++++++++ .../DetailAchievementViewModel.swift | 21 +++++++ .../DetailAchievementViewModelImpl.swift | 58 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift new file mode 100644 index 00000000..26bdd532 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift @@ -0,0 +1,24 @@ +// +// DetailAchievementModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +struct AchieveDetailData: Hashable { + + let missionList: [DailyMissionResponseDTO] + let selectedDate: String + + func formatDateString() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + + guard let date = dateFormatter.date(from: selectedDate) else { return "" } + + dateFormatter.dateFormat = "yyyy년 MM월 dd일" + return dateFormatter.string(from: date) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift new file mode 100644 index 00000000..9efaf74a --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift @@ -0,0 +1,21 @@ +// +// DetailAchievementViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol DetailAchievementViewModelPresentable {} +protocol DetailAchievementViewModel: ViewModel where Input == DetailAchievementViewModelInput, Output == DetailAchievementViewModelOutput {} + +struct DetailAchievementViewModelInput { + let viewWillAppearSubject: PassthroughSubject + let dismissSubject: PassthroughSubject +} + +struct DetailAchievementViewModelOutput { + let viewWillAppearSubject: AnyPublisher<[AchieveDetailData], Never> +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift new file mode 100644 index 00000000..e7571382 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift @@ -0,0 +1,58 @@ +// +// DetailAchievementViewModelImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAchievementViewModelPresentable { + + private weak var coordinator: AchieveCoordinator? + private var missionManager: AchieveManagerImpl + private var selectedDate: String? + private var cancelBag = Set() + + init(coordinator: AchieveCoordinator, missionManager: AchieveManagerImpl) { + self.coordinator = coordinator + self.missionManager = missionManager + } + + let missionsSubject = PassthroughSubject<[AchieveDetailData], Never>() + + func transform(input: DetailAchievementViewModelInput) -> DetailAchievementViewModelOutput { + + input.viewWillAppearSubject + .sink(receiveValue: { [weak self] _ in + guard let self else { return } + guard let selectedDate = self.selectedDate else { return } + self.getDailyMission(date: selectedDate) + }) + .store(in: &cancelBag) + + input.dismissSubject + .sink {[weak self] _ in + self?.coordinator?.dismiss() + } + .store(in: &cancelBag) + + return Output(viewWillAppearSubject: missionsSubject.eraseToAnyPublisher()) + } + + func getDailyMission(date: String) { + missionManager.getDailyMission(date: date) + .sink(receiveCompletion: { event in + print("completion: \(event)") + }, receiveValue: { data in + self.missionsSubject.send([data]) + }) + .store(in: &cancelBag) + } + + func selectedDate(_ date: Date) { + let dateString = Utils.dateFormatterString(format: "YYYY-MM-dd", date: date) + self.selectedDate = dateString + } +} From f7484c1d1460c05dba16dcc32313ece1870a28be Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 18:49:25 +0900 Subject: [PATCH 09/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20MVVM=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DetailAchievementViewController.swift | 95 ++++++++++--------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index a2274ccb..4d818b08 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -6,6 +6,7 @@ // import UIKit +import Combine import Then import SnapKit @@ -16,33 +17,43 @@ final class DetailAchievementViewController: UIViewController { typealias CellRegistration = UICollectionView.CellRegistration typealias HeaderRegistration = UICollectionView.SupplementaryRegistration - typealias Item = DailyMissionResponseDTO + typealias Item = AchieveDetailData typealias DataSource = UICollectionViewDiffableDataSource typealias SnapShot = NSDiffableDataSourceSnapshot - + enum Section: Int, Hashable { case main } - - var selectedDate: Date? - + + private let viewWillAppearSubject = PassthroughSubject() + private let dismissSubject = PassthroughSubject() + private var cancelBag = Set() + private var dataSource: DataSource? - - private lazy var safeArea = self.view.safeAreaLayoutGuide + private var viewModel: any DetailAchievementViewModel // MARK: - UI Components + private lazy var safeArea = self.view.safeAreaLayoutGuide private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) + // MARK: - init + + init(viewModel: some DetailAchievementViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if let selectedDate = selectedDate { - requestDetailAPI(date: Utils.dateFormatterString(format: "YYYY-MM-dd", - date: selectedDate)) - } + viewWillAppearSubject.send(()) } override func viewDidLoad() { @@ -51,7 +62,8 @@ final class DetailAchievementViewController: UIViewController { setUI() setLayout() setDataSource() - setSnapShot() + setSnapShot(with: []) + setBindings() } override func touchesBegan(_ touches: Set, with event: UIEvent?) { @@ -61,7 +73,7 @@ final class DetailAchievementViewController: UIViewController { if !collectionView.frame.contains(location) { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.closeDailyMissionModal) - self.dismiss(animated: true) + dismissSubject.send(()) } } } @@ -94,15 +106,13 @@ extension DetailAchievementViewController { private func setDataSource() { - let cellRegistration = CellRegistration {cell, _, item in - cell.configure(model: item) + let cellRegistration = CellRegistration {cell, index, item in + cell.configure(model: item.missionList[index.item]) } - let headerRegistration = HeaderRegistration(elementKind: UICollectionView.elementKindSectionHeader) { headerView, _, _ in - if let date = self.selectedDate { - headerView.configure(text: Utils.dateFormatterString(format: "YYYY년 MM월 dd일", - date: date)) - } + let headerRegistration = HeaderRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, _, indexPath in + guard let item = self?.dataSource?.itemIdentifier(for: indexPath) else { return } + headerView.configure(text: item.formatDateString()) } dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in @@ -114,28 +124,30 @@ extension DetailAchievementViewController { dataSource?.supplementaryViewProvider = { collectionView, _, indexPath in return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + } } - private func setSnapShot() { + private func setBindings() { + let input = DetailAchievementViewModelInput(viewWillAppearSubject: viewWillAppearSubject, dismissSubject: dismissSubject) - var snapShot = SnapShot() - defer { - dataSource?.apply(snapShot, animatingDifferences: false) - } + let output = viewModel.transform(input: input) - snapShot.appendSections([.main]) - snapShot.appendItems([], toSection: .main) + output.viewWillAppearSubject + .receive(on: RunLoop.main) + .sink { [weak self] item in + self?.setSnapShot(with: item) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.appearDailyMissionModal(total: item.count)) + } + .store(in: &cancelBag) } - private func updateData(item: [DailyMissionResponseDTO]) { - - guard var snapshot = dataSource?.snapshot() else { return } - - snapshot.appendItems(item, toSection: .main) - dataSource?.apply(snapshot) + private func setSnapShot(with items: [AchieveDetailData]) { + var snapShot = SnapShot() + snapShot.appendSections([.main]) + snapShot.appendItems(items, toSection: .main) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.appearDailyMissionModal(total: item.count)) + dataSource?.applySnapshotUsingReloadData(snapShot) } private func layout() -> UICollectionViewCompositionalLayout { @@ -149,9 +161,9 @@ extension DetailAchievementViewController { leading: 20, bottom: 0, trailing: 20) - config.itemSeparatorHandler = { indexPath, config in + config.itemSeparatorHandler = { [weak self] indexPath, config in var config = config - guard let itemCount = self.dataSource?.snapshot().itemIdentifiers(inSection: .main).count else { return config } + guard let itemCount = self?.dataSource?.snapshot().itemIdentifiers(inSection: .main).count else { return config } let isLastItem = indexPath.item == itemCount - 1 config.bottomSeparatorVisibility = isLastItem ? .hidden : .visible return config @@ -160,14 +172,3 @@ extension DetailAchievementViewController { return UICollectionViewCompositionalLayout.list(using: config) } } - -extension DetailAchievementViewController { - private func requestDetailAPI(date: String) { - MissionAPI.shared.getDailyMission(date: date) { [weak self] response in - guard let self else { return } - guard let response = response else { return } - guard let data = response.data else { return } - self.updateData(item: data) - } - } -} From 0b3bc34ee1f4dbf0dba0594cd349a5d5130fae3b Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 23:43:19 +0900 Subject: [PATCH 10/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20=EA=B4=80=EB=A0=A8=20network=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/Mission/MissionAPI.swift | 7 ++++++- .../Network/Manager/AchieveManagerImpl.swift | 15 +++++++++++++++ .../Manager/ManagerInterface/AchieveManager.swift | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift index 09f8a39f..bc04dddd 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift @@ -15,13 +15,18 @@ typealias DefaultMissionAPI = BaseAPI // 전체 수정 후 - 네이밍 변경 MissionAPI protocol MissionAPIProtocol { func getDailyMission(date: String) -> AnyPublisher + func getAchieveCalendar(month: String) -> AnyPublisher } extension DefaultMissionAPI: MissionAPIProtocol { - + func getDailyMission(date: String) -> AnyPublisher { return requestWithCombine(MissionService.dailyMission(date: date)) } + + func getAchieveCalendar(month: String) -> AnyPublisher { + return requestWithCombine(MissionService.achieveCalendar(month: month)) + } } typealias DailyMissionData = GeneralArrayResponse diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift index 3fe3a19c..54c768aa 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -24,4 +24,19 @@ final class AchieveManagerImpl: AchieveManager { } .eraseToAnyPublisher() } + + func getAchieveCalendar(month: String) -> AnyPublisher { + return missionAPI.getAchieveCalendar(month: month) + .map { response in + self.convertResponseToCalendarEventData(response) + } + .compactMap { $0 } + .eraseToAnyPublisher() + } + + private func convertResponseToCalendarEventData(_ response: CalendarData) -> CalendarEventData? { + guard let data = response.data else { return nil } + let calendarData = Dictionary(uniqueKeysWithValues: data.map { ($0.actionDate, $0.percentage) }) + return CalendarEventData(percentages: calendarData) + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift index fdbb718c..16c32aca 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift @@ -10,4 +10,5 @@ import Combine protocol AchieveManager { func getDailyMission(date: String) -> AnyPublisher + func getAchieveCalendar(month: String) -> AnyPublisher } From 8baf42f73f151af3c1940da28e9cfd590f6f5d34 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 23:44:28 +0900 Subject: [PATCH 11/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20ViewModel=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Achievement/Model/AchievementModel.swift | 13 +++++ .../ViewModel/AchievementViewModel.swift | 22 +++++++ .../ViewModel/AchievementViewModelImpl.swift | 57 +++++++++++++++++++ .../DetailAchievementViewModelImpl.swift | 11 ++-- 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift new file mode 100644 index 00000000..94934409 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift @@ -0,0 +1,13 @@ +// +// AchievementModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +struct CalendarEventData { + + let percentages: [String: Float] +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift new file mode 100644 index 00000000..22b295ac --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift @@ -0,0 +1,22 @@ +// +// AchievementViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol AchievementViewModelPresentable {} +protocol AchievementViewModel: ViewModel where Input == AchievementViewModelInput, Output == AchievementViewModelOutput {} + +struct AchievementViewModelInput { + let viewWillAppearSubject: PassthroughSubject + let calendarCellTapped: PassthroughSubject + let currentMonthSubject: PassthroughSubject +} + +struct AchievementViewModelOutput { + let viewWillAppearSubject: AnyPublisher +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift new file mode 100644 index 00000000..540bf2fd --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift @@ -0,0 +1,57 @@ +// +// AchievementViewModelImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class AchievementViewModelImpl: AchievementViewModel, AchievementViewModelPresentable { + + private weak var coordinator: AchieveCoordinator? + private var manager: AchieveManager + private var month: String? + private var cancelBag = Set() + + init(coordinator: AchieveCoordinator, manager: AchieveManager) { + self.coordinator = coordinator + self.manager = manager + } + + let eventSubject = PassthroughSubject() + + func transform(input: AchievementViewModelInput) -> AchievementViewModelOutput { + + Publishers.CombineLatest(input.viewWillAppearSubject, input.currentMonthSubject) + .map { _, month in month } + .removeDuplicates() + .sink { [weak self] month in + guard let self = self else { return } + let monthString = month.formattedString(format: "yyyy-MM") + self.month = monthString + self.getCalendarEvent(month: monthString) + } + .store(in: &cancelBag) + + input.calendarCellTapped + .sink { [weak self] date in + self?.coordinator?.showAchieveDetailViewController(selectedDate: date) + } + .store(in: &cancelBag) + + return Output(viewWillAppearSubject: eventSubject.eraseToAnyPublisher()) + } + + func getCalendarEvent(month: String) { + manager.getAchieveCalendar(month: month) + .sink(receiveCompletion: { event in + print("completion: \(event)") + }, receiveValue: { data in + dump(data) + self.eventSubject.send(data) + }) + .store(in: &cancelBag) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift index e7571382..45abc435 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift @@ -11,13 +11,13 @@ import Combine final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAchievementViewModelPresentable { private weak var coordinator: AchieveCoordinator? - private var missionManager: AchieveManagerImpl + private var manager: AchieveManager private var selectedDate: String? private var cancelBag = Set() - init(coordinator: AchieveCoordinator, missionManager: AchieveManagerImpl) { + init(coordinator: AchieveCoordinator, manager: AchieveManager) { self.coordinator = coordinator - self.missionManager = missionManager + self.manager = manager } let missionsSubject = PassthroughSubject<[AchieveDetailData], Never>() @@ -42,7 +42,7 @@ final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAc } func getDailyMission(date: String) { - missionManager.getDailyMission(date: date) + manager.getDailyMission(date: date) .sink(receiveCompletion: { event in print("completion: \(event)") }, receiveValue: { data in @@ -52,7 +52,6 @@ final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAc } func selectedDate(_ date: Date) { - let dateString = Utils.dateFormatterString(format: "YYYY-MM-dd", date: date) - self.selectedDate = dateString + self.selectedDate = date.formattedString(format: "YYYY-MM-dd") } } From 03b525df6b823c2179d127b45dbfdaa0303b52d2 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 23:44:49 +0900 Subject: [PATCH 12/21] =?UTF-8?q?[Feat]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20MVVM=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Factory/ViewControllerFactory.swift | 7 +- .../AchievementViewController.swift | 108 ++++++++---------- 2 files changed, 53 insertions(+), 62 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift index 1ffa93d8..13e88600 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift @@ -204,14 +204,17 @@ extension ViewControllerFactoryImpl { // achieve extension ViewControllerFactoryImpl { func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { - let viewController = AchievementViewController(coordinator: coordinator) + let missionAPI = DefaultMissionAPI() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = AchievementViewModelImpl(coordinator: coordinator, manager: manager) + let viewController = AchievementViewController(viewModel: viewModel) return viewController } func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController { let missionAPI = DefaultMissionAPI() let manager = AchieveManagerImpl(missionAPI: missionAPI) - let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, missionManager: manager) + let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, manager: manager) viewModel.selectedDate(date) let viewController = DetailAchievementViewController(viewModel: viewModel) return viewController diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index f4226303..732f2f3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -16,12 +16,18 @@ final class AchievementViewController: UIViewController { // MARK: - Properties + private let viewWillAppearSubject = PassthroughSubject() + private let eventCellSubject = PassthroughSubject() + private let monthSubject = PassthroughSubject() + private weak var coordinator: AchieveCoordinator? - private var currentPage = Date() private var dataSource: [String: Float] = [:] private lazy var safeArea = self.view.safeAreaLayoutGuide + private let viewModel: any AchievementViewModel + private var cancelBag = Set() + // MARK: - UI Components private let scrollView = UIScrollView() @@ -30,8 +36,9 @@ final class AchievementViewController: UIViewController { private let statisticsView = StatisticsView() // MARK: - init - init(coordinator: AchieveCoordinator) { - self.coordinator = coordinator + + init(viewModel: some AchievementViewModel) { + self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } @@ -43,13 +50,12 @@ final class AchievementViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) - if let today = monthCalendar.today() { - monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) - monthCalendar.currentPage(date: today) - requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: today)) - } + viewWillAppearSubject.send(()) + monthSubject.send(Date()) + monthCalendar.currentPage(date: Date()) + monthCalendar.configureYearMonth(to: Date().formattedString(format: I18N.yearMonthTitle)) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) } override func viewDidLoad() { @@ -57,6 +63,7 @@ final class AchievementViewController: UIViewController { setUI() setLayout() + setBindings() } } @@ -79,9 +86,8 @@ extension AchievementViewController { } monthCalendar.do { - $0.layer.cornerRadius = 12 - $0.layer.borderWidth = 1 - $0.layer.borderColor = UIColor.gray4?.cgColor + $0.makeCornerRound(radius: 12) + $0.makeBorder(width: 1, color: .gray4!) $0.configure(delegate: self, datasource: self) } } @@ -113,86 +119,68 @@ extension AchievementViewController { $0.bottom.equalTo(scrollView.snp.bottom).inset(20) } } + + func setBindings() { + let input = AchievementViewModelInput(viewWillAppearSubject: viewWillAppearSubject, + calendarCellTapped: eventCellSubject, + currentMonthSubject: monthSubject) + + let output = viewModel.transform(input: input) + + output.viewWillAppearSubject + .receive(on: RunLoop.main) + .sink { [weak self] item in + self?.dataSource = item.percentages + self?.monthCalendar.reloadCollectionView() + } + .store(in: &cancelBag) + } } extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - self.currentPage = calendar.currentPage - requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) - monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) + self.monthSubject.send(calendar.currentPage) + monthCalendar.configureYearMonth(to: calendar.currentPage.formattedString(format: I18N.yearMonthTitle)) } func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { - Utils.dateFormatterString(format: "dd", date: date) + date.formattedString(format: "dd") } func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { calendar.appearance.selectionColor = .clear calendar.appearance.titleSelectionColor = .white - let dateString = Utils.dateFormatterString(format: "yyyy-MM-dd", date: date) - if self.dataSource.contains(where: { $0.key == dateString }) { - coordinator?.showAchieveDetailViewController(selectedDate: date) + + if self.dataSource.contains(where: { $0.key == date.formattedString() }) { + self.eventCellSubject.send(date) } } - func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { - Calendar.current.isDate(date, equalTo: calendar.currentPage, toGranularity: .month) - } - func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { - titleColorFor(date: date) + if let percentage = self.dataSource[date.formattedString()], percentage == 1.0 { + return .black + } + return .white } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - let currentMonth = Calendar.current.component(.month, from: currentPage) + let currentMonth = Calendar.current.component(.month, from: calendar.currentPage) let dateMonth = Calendar.current.component(.month, from: date) - if let percentage = getPercentage(for: date) { - return percentage == 1.0 ? .black : .white - } else { + guard let percentage = self.dataSource[date.formattedString()] else { return currentMonth != dateMonth ? .gray3 : .white } + return percentage == 1.0 ? .black : .white } func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - guard let percentage = getPercentage(for: date) else { return cell } + guard let percentage = self.dataSource[date.formattedString()] else { return cell } cell.configure(percent: percentage) return cell } } - -// MARK: - Others - -extension AchievementViewController { - - func requestMonthAPI(month: String) { - MissionAPI.shared.getAchieveCalendar(month: month) { [weak self] response in - - guard let self, let response = response, let data = response.data else { return } - - let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } - self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) - self.monthCalendar.calendar.collectionView.reloadData() - } - } - - private func getPercentage(for date: Date) -> Float? { - - let dateString = Utils.dateFormatterString(format: nil, date: date) - return self.dataSource[dateString] - } - - private func titleColorFor(date: Date) -> UIColor { - - if let percentage = getPercentage(for: date) { - return percentage == 1.0 ? .black : .white - } - - return .white - } -} From 5142471c41a9454d23645a0a1661d081ae8d0f27 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Tue, 12 Mar 2024 23:45:26 +0900 Subject: [PATCH 13/21] =?UTF-8?q?[Fix]=20#241=20-=20UI=20=EB=A1=9C?= =?UTF-8?q?=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 --- .../Global/Extensions/UIView+.swift | 18 ++++++++++++++++++ .../DetailAchievementCollectionViewCell.swift | 2 +- .../DetailAchievementViewController.swift | 2 +- .../Cells/ActionCollectionViewCell.swift | 5 ++--- .../Cells/DateCollectionViewCell.swift | 5 ++--- .../Cells/GoalCollectionViewCell.swift | 7 +++---- .../Cells/NottodoCollectionViewCell.swift | 5 ++--- .../RecommendKeywordCollectionViewCell.swift | 2 +- .../Cells/SituationCollectionViewCell.swift | 5 ++--- .../AddMissionViewController.swift | 2 +- .../Presentation/Auth/AuthButtonView.swift | 2 +- .../Common/Calendar/CalendarView.swift | 15 ++++++++------- .../CommonNotificationViewController.swift | 8 ++++---- .../Common/Modal/DeleteModalView.swift | 2 +- .../Common/Modal/QuitModalView.swift | 2 +- .../Common/Modal/WithdrawModalView.swift | 4 ++-- .../Common/Toast/NottodoToastView.swift | 2 +- .../Presentation/Common/Utils.swift | 9 ++++++++- .../MissionDetailCollectionViewCell.swift | 9 +++------ .../Cells/MissionListCollectionViewCell.swift | 6 +++--- .../DetailCalendarViewController.swift | 2 +- .../MissionDetailViewController.swift | 2 +- .../MyInfoAccountStackView.swift | 2 +- .../MyInfoAccountViewController.swift | 4 ++-- .../NotificationDialogViewController.swift | 6 +++--- .../Cell/OnboardingCollectionViewCell.swift | 6 ++---- .../Cell/SubOnboardingCollectionViewCell.swift | 6 ++---- .../LogoOnboardingViewController.swift | 2 +- .../ThirdOnboardingViewController.swift | 2 +- .../Cells/RecommendCollectionViewCell.swift | 6 ++---- .../RecommendViewController.swift | 2 +- .../RecommendActionCollectionViewCell.swift | 6 ++---- .../Cells/RecommendActionHeaderView.swift | 3 +-- 33 files changed, 85 insertions(+), 76 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift index 8409d7be..c3580482 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift @@ -13,4 +13,22 @@ extension UIView { func addSubviews(_ views: UIView...) { views.forEach { self.addSubview($0) } } + + func makeCornerRound (radius: CGFloat) { + layer.cornerRadius = radius + layer.masksToBounds = true + } + + func makeBorder(width: CGFloat, color: UIColor) { + layer.borderWidth = width + layer.borderColor = color.cgColor + } + + func roundCorners(corners: UIRectCorner, radius: CGFloat) { + let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) + let mask = CAShapeLayer() + mask.path = path.cgPath + layer.mask = mask + } + } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift index eb2feeda..4cd928df 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift @@ -48,7 +48,7 @@ extension DetailAchievementCollectionViewCell { $0.layer.backgroundColor = UIColor.bg?.cgColor $0.font = .Pretendard(.medium, size: 14) $0.textColor = .gray1 - $0.layer.cornerRadius = 25/2 + $0.makeCornerRound(radius: 25/2) } titleLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 4d818b08..485c5e72 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -87,7 +87,7 @@ extension DetailAchievementViewController { collectionView.do { $0.collectionViewLayout = layout() - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) $0.backgroundColor = .white $0.bounces = false $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift index f98cadae..97290817 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift @@ -76,9 +76,8 @@ final class ActionCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension ActionCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical foldStackView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift index 09ad886b..da7f522e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift @@ -92,9 +92,8 @@ final class DateCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension DateCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) calendarImage.image = .icCalendar dayLabel.font = .Pretendard(.medium, size: 15) stackView.axis = .vertical diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift index 6ae137fa..c3b945bf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift @@ -81,9 +81,8 @@ final class GoalCollectionViewCell: UICollectionViewCell, AddMissionMenu { private extension GoalCollectionViewCell { func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical [nottodoStackView, goalStackView].forEach { @@ -109,7 +108,7 @@ private extension GoalCollectionViewCell { [nottodoTag, goalTag].forEach { $0.backgroundColor = .gray2 - $0.layer.cornerRadius = 20 / 2 + $0.makeCornerRound(radius: 20 / 2) $0.clipsToBounds = true $0.textColor = .gray4 $0.font = .Pretendard(.medium, size: 12) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift index 7896a441..4d6d8bcd 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift @@ -76,9 +76,8 @@ final class NottodoCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension NottodoCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) historyCollectionView.backgroundColor = .clear historyCollectionView.indicatorStyle = .white checkImage.image = .icChecked diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift index e1303e65..5913f6f8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift @@ -43,7 +43,7 @@ final class RecommendKeywordCollectionViewCell: UICollectionViewCell { private extension RecommendKeywordCollectionViewCell { func setUI() { - contentView.layer.cornerRadius = contentView.frame.height / 2 + contentView.makeCornerRound(radius: contentView.frame.height / 2) contentView.clipsToBounds = true recommendLabel.do { $0.backgroundColor = .gray2 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift index 2c14ff74..57d04c4c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift @@ -76,9 +76,8 @@ final class SituationCollectionViewCell: UICollectionViewCell, AddMissionMenu { private extension SituationCollectionViewCell { func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical foldStackView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift index d42a9bf7..c793be6c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift @@ -159,7 +159,7 @@ extension AddMissionViewController { } addButton.do { - $0.layer.cornerRadius = 26 / 2 + $0.makeCornerRound(radius: 26 / 2) $0.titleLabel?.font = .Pretendard(.medium, size: 15) $0.setTitleColor(.gray3, for: .disabled) $0.setTitleColor(.gray1, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift index f639d8a2..e17f2b35 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift @@ -39,7 +39,7 @@ extension AuthButtonView { buttonView.do { $0.backgroundColor = color - $0.layer.cornerRadius = 5 + $0.makeCornerRound(radius: 5) } buttonIcon.image = icon diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index bf1c35e8..74595132 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -23,11 +23,11 @@ protocol MonthCalendarDelegate: AnyObject { final class CalendarView: UIView { // MARK: - Properties - + private enum CalendarMoveType { case previous case next - + var monthOffset: Int { switch self { case .previous: @@ -37,7 +37,7 @@ final class CalendarView: UIView { } } } - + weak var delegate: WeekCalendarDelegate? // MARK: - UI Components @@ -75,7 +75,7 @@ extension CalendarView { yearMonthLabel.do { $0.font = .Pretendard(.medium, size: 18) $0.textColor = .white - $0.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: Date()) + $0.text = Date().formattedString(format: I18N.yearMonthTitle) } todayButton.do { @@ -112,6 +112,7 @@ extension CalendarView { } calendar.do { + $0.currentPage = Date() $0.collectionView.register(MissionCalendarCell.self, forCellWithReuseIdentifier: MissionCalendarCell.identifier) } @@ -171,7 +172,7 @@ extension CalendarView { $0.bottom.equalToSuperview().inset(25) } } - + private func changeMonth(with type: CalendarMoveType) { let calendar = Calendar.current let currentPage = self.calendar.currentPage @@ -233,9 +234,9 @@ extension CalendarView { $0.bottom.equalToSuperview().inset(45) } } - + func today() -> Date? { - return calendar.today + return calendar.today } func currentPage(date: Date) { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift index 5a046d73..8adf44f7 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift @@ -61,7 +61,7 @@ extension CommonNotificationViewController { backgroundView.do { $0.backgroundColor = .white - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) } titleLabel.do { @@ -96,7 +96,7 @@ extension CommonNotificationViewController { bottomView.do { $0.backgroundColor = .gray5 $0.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) } blackButton.do { @@ -104,7 +104,7 @@ extension CommonNotificationViewController { $0.setTitle(I18N.commonModalTitle, for: .normal) $0.setTitleColor(.white, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 13) - $0.layer.cornerRadius = 20 + $0.makeCornerRound(radius: 20) $0.addTarget(self, action: #selector(didFormButtonTap), for: .touchUpInside) } @@ -113,7 +113,7 @@ extension CommonNotificationViewController { $0.setTitle(I18N.commonModalTitle, for: .normal) $0.setTitleColor(.ntdBlack, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 20) - $0.layer.cornerRadius = 8 + $0.makeCornerRound(radius: 8) $0.addTarget(self, action: #selector(didFormButtonTap), for: .touchUpInside) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift index 36b2f97f..33c192fa 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift @@ -45,7 +45,7 @@ final class DeleteModalView: UIView { extension DeleteModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) trashImage.do { $0.image = .icTrashbin diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift index 1c5f1623..385013a0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift @@ -57,7 +57,7 @@ extension QuitModalView { extension QuitModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) modalImageView.image = .quit1 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift index 04ed54c2..96828ac6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift @@ -47,7 +47,7 @@ extension WithdrawModalView { extension WithdrawModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) modalImageView.image = .quit2 modalTitleLabel.do { @@ -69,7 +69,7 @@ extension WithdrawModalView { surveyButton.do { $0.setTitle(I18N.surveyButton, for: .normal) $0.backgroundColor = .black - $0.layer.cornerRadius = 7 + $0.makeCornerRound(radius: 7) $0.titleLabel?.font = .Pretendard(.medium, size: 14) $0.setTitleColor(.white, for: .normal) $0.addTarget(self, action: #selector(buttonDidTapped), for: .touchUpInside) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift index a1ea1804..9b2633b6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift @@ -36,7 +36,7 @@ final class NottodoToastView: UIView { extension NottodoToastView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 12 + makeCornerRound(radius: 12) layer.makeShadow(color: .black, alpha: 0.25, x: 0, y: 4, blur: 30, spread: 0) isUserInteractionEnabled = false diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift index cb953f00..ed4a78c4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift @@ -87,4 +87,11 @@ final class Utils { } } - +extension Date { + func formattedString(format: String? = nil, locale: Locale = Locale(identifier: "ko_KR")) -> String { + let formatter = DateFormatter() + formatter.dateFormat = format ?? "yyyy-MM-dd" + formatter.locale = locale + return formatter.string(from: self) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift index ca1475b1..ca51915f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift @@ -49,8 +49,7 @@ extension MissionDetailCollectionViewCell { missionTagLabel.do { $0.backgroundColor = .bg - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 12.5 + $0.makeCornerRound(radius: 12.5) $0.font = .Pretendard(.medium, size: 14) $0.textColor = .gray1 } @@ -63,14 +62,12 @@ extension MissionDetailCollectionViewCell { accumulateView.do { $0.backgroundColor = .green2?.withAlphaComponent(0.3) - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 34 + $0.makeCornerRound(radius: 34) } accumulateSubView.do { $0.backgroundColor = .green2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 28 + $0.makeCornerRound(radius: 28) } accumulateLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift index 6063f709..09ce1a15 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift @@ -35,7 +35,7 @@ final class MissionListCollectionViewCell: UICollectionViewCell { override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) - contentView.layer.cornerRadius = 10 + contentView.makeCornerRound(radius: 10) } override init(frame: CGRect) { @@ -63,7 +63,7 @@ extension MissionListCollectionViewCell { backgroundColor = .clear contentView.backgroundColor = .white contentView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner] - contentView.layer.cornerRadius = 10 + contentView.makeCornerRound(radius: 10) checkButton.do { $0.backgroundColor = .clear @@ -75,7 +75,7 @@ extension MissionListCollectionViewCell { tagLabel.do { $0.backgroundColor = .bg $0.clipsToBounds = true - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) $0.textColor = isTapped ? .gray7 : .gray1 $0.font = .Pretendard(.medium, size: 14) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 90f40c2f..c0fe31b1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -91,7 +91,7 @@ extension DetailCalendarViewController { } monthCalendar.do { - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) $0.configure(delegate: self, datasource: self) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 5e6baa7e..d284db3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -86,7 +86,7 @@ extension MissionDetailViewController { headerView.do { $0.backgroundColor = .white $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) } collectionView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift index 1dba8e4e..8a8e8022 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift @@ -55,7 +55,7 @@ final class MyInfoAccountStackView: UIView { extension MyInfoAccountStackView { private func setUI(title: String, isHidden: Bool) { - layer.cornerRadius = 10 + makeCornerRound(radius: 10) titleLabel.do { $0.text = title diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift index c67018d0..55778c89 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift @@ -81,12 +81,12 @@ private extension MyInfoAccountViewController { $0.axis = .vertical $0.spacing = 0 $0.distribution = .equalSpacing - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) $0.backgroundColor = .gray1 } logoutView.do { - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) $0.backgroundColor = .gray1 } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index f5af2feb..92e6e2b1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -75,7 +75,7 @@ extension NotificationDialogViewController { } buttonStackView.do { - $0.layer.cornerRadius = 17 + $0.makeCornerRound(radius: 17) $0.backgroundColor = .none } @@ -93,7 +93,7 @@ extension NotificationDialogViewController { bottomButton.do { $0.backgroundColor = .white - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(.black, for: .normal) $0.setTitle(I18N.notiDialogButton, for: .normal) @@ -102,7 +102,7 @@ extension NotificationDialogViewController { alertView.do { $0.backgroundColor = .none - $0.layer.cornerRadius = 17 + $0.makeCornerRound(radius: 17) } verticalView.backgroundColor = .notiGreen diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift index c6f26905..54025edc 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift @@ -45,10 +45,8 @@ final class OnboardingCollectionViewCell: UICollectionViewCell { extension OnboardingCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.cornerRadius = 10 - contentView.layer.masksToBounds = true - contentView.layer.borderWidth = 1 - contentView.layer.borderColor = UIColor.clear.cgColor + contentView.makeCornerRound(radius: 10) + contentView.makeBorder(width: 1, color: .clear) titleLabel.do { $0.font = .Pretendard(.regular, size: 15) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift index 4a4446b6..c6a3f432 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift @@ -39,12 +39,10 @@ final class SubOnboardingCollectionViewCell: UICollectionViewCell { extension SubOnboardingCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.cornerRadius = 35 - contentView.layer.masksToBounds = true + contentView.makeCornerRound(radius: 35) tagLabel.do { - $0.layer.cornerRadius = 8 - $0.layer.masksToBounds = true + $0.makeCornerRound(radius: 8) $0.backgroundColor = UIColor.gray8 $0.font = .Pretendard(.regular, size: 10) $0.textColor = .gray6 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift index ac7e72e4..06ca87ba 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift @@ -87,7 +87,7 @@ extension LogoOnboardingViewController { nextButton.do { $0.isHidden = true $0.backgroundColor = .white - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(.black, for: .normal) $0.setTitle(I18N.firstButton, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift index b798cca5..fe83a872 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift @@ -73,7 +73,7 @@ extension ThirdOnboardingViewController { nextButton.do { $0.backgroundColor = isTapped ? .white : .gray2 $0.isUserInteractionEnabled = isTapped - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(isTapped ? .black :.gray4, for: .normal) $0.setTitle(I18N.thirdButton, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift index e2128a61..2d6692bf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift @@ -45,16 +45,14 @@ extension RecommendCollectionViewCell { private func setUI() { contentView.do { $0.backgroundColor = .gray1 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) } tagLabel.do { $0.font = .Pretendard(.medium, size: 14) $0.textColor = .white $0.backgroundColor = .gray2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 25 / 2 + $0.makeCornerRound(radius: 25 / 2) } titleLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift index 32abdf86..b52f0f79 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift @@ -89,7 +89,7 @@ private extension RecommendViewController { $0.setTitle(I18N.addAction, for: .normal) $0.setTitleColor(.ntdBlack, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift index 3eb3684d..2437fd2a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift @@ -49,10 +49,8 @@ extension RecommendActionCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.masksToBounds = true - contentView.layer.cornerRadius = 10 - contentView.layer.borderWidth = 1 - contentView.layer.borderColor = UIColor.clear.cgColor + contentView.makeCornerRound(radius: 10) + contentView.makeBorder(width: 1, color: .clear) stackView.axis = .vertical titleLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift index 79d660a6..713cb473 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift @@ -52,8 +52,7 @@ extension RecommendActionHeaderView { $0.font = .Pretendard(.medium, size: 14) $0.textColor = .white $0.backgroundColor = .gray2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 25 / 2 + $0.makeCornerRound(radius: 25 / 2) } titleLabel.do { From cc045cb5431c723223d169ab40e6efb2a907d212 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 13 Mar 2024 02:46:12 +0900 Subject: [PATCH 14/21] =?UTF-8?q?[Fix]=20#241=20=3D=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20UI=20=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 --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 217 ++++++++++++++++-- .../Coordinator/AchieveCoordinatorImpl.swift | 2 +- .../Factory/ViewControllerFactory.swift | 4 +- .../Protocol/AchieveCoordinator.swift | 2 +- .../Network/Manager/AchieveManagerImpl.swift | 11 +- .../ManagerInterface/AchieveManager.swift | 2 +- .../Achievement/Model/AchievementModel.swift | 2 +- .../AchievementViewController.swift | 22 +- .../ViewModel/AchievementViewModel.swift | 2 +- .../ViewModel/AchievementViewModelImpl.swift | 24 +- .../DetailAchievementViewModel.swift | 4 +- .../DetailAchievementViewModelImpl.swift | 10 +- 12 files changed, 239 insertions(+), 63 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index a7b50210..060d6d28 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -27,8 +27,6 @@ 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */; }; 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */; }; 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */; }; - 095FEE162B9ED28800FF44C0 /* DetailAchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */; }; - 095FEE182B9ED30A00FF44C0 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */; }; 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; }; 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */; }; 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */; }; @@ -38,6 +36,24 @@ 0982DE5829AE40FB00D933D2 /* UITabBar+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982DE5729AE40FB00D933D2 /* UITabBar+.swift */; }; 0982DE5A29AE5E6000D933D2 /* CompositionalLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982DE5929AE5E6000D933D2 /* CompositionalLayout.swift */; }; 0987C8402B9DD4DC007EE8DE /* MissionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0987C83F2B9DD242007EE8DE /* MissionService.swift */; }; + 098904292B81AF3E004AAD3C /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 098904282B81AF3E004AAD3C /* API_KEY.plist */; }; + 098904302B81BB3A004AAD3C /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989042F2B81BB3A004AAD3C /* Coordinator.swift */; }; + 098904322B81BB43004AAD3C /* CoordinatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904312B81BB43004AAD3C /* CoordinatorDelegate.swift */; }; + 098904352B81BB85004AAD3C /* CoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904342B81BB85004AAD3C /* CoordinatorType.swift */; }; + 098904382B81BC16004AAD3C /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904372B81BC16004AAD3C /* AppCoordinator.swift */; }; + 0989043B2B81BCFA004AAD3C /* AppCoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989043A2B81BCFA004AAD3C /* AppCoordinatorImpl.swift */; }; + 0989043E2B81BD50004AAD3C /* CoordinatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989043D2B81BD50004AAD3C /* CoordinatorFactory.swift */; }; + 098904402B81BFAF004AAD3C /* ViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989043F2B81BFAF004AAD3C /* ViewControllerFactory.swift */; }; + 098904442B81C18A004AAD3C /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904432B81C18A004AAD3C /* HomeCoordinator.swift */; }; + 098904462B81C191004AAD3C /* MypageCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904452B81C191004AAD3C /* MypageCoordinator.swift */; }; + 098904482B81C197004AAD3C /* AchieveCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904472B81C197004AAD3C /* AchieveCoordinator.swift */; }; + 0989044C2B81C210004AAD3C /* HomecoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989044B2B81C210004AAD3C /* HomecoordinatorImpl.swift */; }; + 0989044E2B81C216004AAD3C /* MypageCoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989044D2B81C216004AAD3C /* MypageCoordinatorImpl.swift */; }; + 098904502B81C21D004AAD3C /* AchieveCoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0989044F2B81C21D004AAD3C /* AchieveCoordinatorImpl.swift */; }; + 098904522B81CA33004AAD3C /* UpdateCoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904512B81CA33004AAD3C /* UpdateCoordinatorImpl.swift */; }; + 098904542B81CA47004AAD3C /* UpdateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098904532B81CA47004AAD3C /* UpdateCoordinator.swift */; }; + 098A23A22B833C6700265955 /* AuthCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098A23A12B833C6700265955 /* AuthCoordinator.swift */; }; + 098A23A42B833F0300265955 /* AuthCoordinatorImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098A23A32B833F0300265955 /* AuthCoordinatorImpl.swift */; }; 098BFD5929B7999E008E80F9 /* MyProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BFD5829B7999E008E80F9 /* MyProfileCollectionViewCell.swift */; }; 098BFD5B29B79B6A008E80F9 /* MyInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BFD5A29B79B6A008E80F9 /* MyInfoModel.swift */; }; 098BFD5D29B79CE3008E80F9 /* InfoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BFD5C29B79CE3008E80F9 /* InfoCollectionViewCell.swift */; }; @@ -46,10 +62,21 @@ 099FC98329B30A2E005B37E6 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98229B30A2E005B37E6 /* Utils.swift */; }; 099FC98929B3233D005B37E6 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98829B3233D005B37E6 /* CalendarView.swift */; }; 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */; }; - 09A8E48C2B9DBE5C00C0F48F /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 09A8E48B2B9DBE5C00C0F48F /* API_KEY.plist */; }; 09A8E48E2B9DBEC700C0F48F /* BaseAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */; }; 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56032B09F23800526C8C /* HomeDataSource.swift */; }; + 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */; }; + 09DCB84F2BA0147500B6BB74 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */; }; + 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */; }; + 09DCB8562BA0308D00B6BB74 /* APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8552BA0308D00B6BB74 /* APIError.swift */; }; + 09DCB8582BA0309F00B6BB74 /* ErrorReponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */; }; + 09DCB85A2BA030DB00B6BB74 /* BaseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8592BA030DB00B6BB74 /* BaseService.swift */; }; + 09DCB8602BA031E000B6BB74 /* AchieveManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */; }; + 09DCB8622BA031F600B6BB74 /* AchieveManagerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */; }; + 09DCB8652BA056C800B6BB74 /* DetailAchievementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */; }; + 09DCB8672BA05F5400B6BB74 /* AchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */; }; + 09DCB8692BA05F9E00B6BB74 /* AchievementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */; }; + 09DCB86B2BA0600600B6BB74 /* AchievementViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */; }; 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; }; 09DCCD232A18EFB0003DCF8A /* MissionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD222A18EFB0003DCF8A /* MissionAPI.swift */; }; 09ED941B2B2ABAB7001864EF /* CommonNotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09ED941A2B2ABAB7001864EF /* CommonNotificationViewController.swift */; }; @@ -162,9 +189,6 @@ 6CF4705B29A68EA9008D145C /* URLConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705A29A68EA9008D145C /* URLConstant.swift */; }; 6CF4705D29A68F5C008D145C /* NetworkLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */; }; 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705E29A69025008D145C /* GeneralResponse.swift */; }; - 6CF4706129A69096008D145C /* NetworkConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706029A69096008D145C /* NetworkConstant.swift */; }; - 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706229A690CD008D145C /* NetworkResult.swift */; }; - 6CF4706529A690E5008D145C /* NetworkBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706429A690E5008D145C /* NetworkBase.swift */; }; 6CF4706A29A71D71008D145C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706929A71D71008D145C /* Strings.swift */; }; 6CF4706D29A739D9008D145C /* RecommendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706C29A739D9008D145C /* RecommendViewController.swift */; }; 6CF4707029A73A15008D145C /* RecommendActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706F29A73A15008D145C /* RecommendActionViewController.swift */; }; @@ -192,8 +216,6 @@ 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewController.swift; sourceTree = ""; }; 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = ""; }; 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsView.swift; sourceTree = ""; }; - 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModel.swift; sourceTree = ""; }; - 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtil.swift; sourceTree = ""; }; 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultKeys.swift; sourceTree = ""; }; 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = ""; }; @@ -203,6 +225,24 @@ 0982DE5729AE40FB00D933D2 /* UITabBar+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabBar+.swift"; sourceTree = ""; }; 0982DE5929AE5E6000D933D2 /* CompositionalLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionalLayout.swift; sourceTree = ""; }; 0987C83F2B9DD242007EE8DE /* MissionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionService.swift; sourceTree = ""; }; + 098904282B81AF3E004AAD3C /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = API_KEY.plist; path = ../../../../Desktop/API_KEY.plist; sourceTree = ""; }; + 0989042F2B81BB3A004AAD3C /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; + 098904312B81BB43004AAD3C /* CoordinatorDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatorDelegate.swift; sourceTree = ""; }; + 098904342B81BB85004AAD3C /* CoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatorType.swift; sourceTree = ""; }; + 098904372B81BC16004AAD3C /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; + 0989043A2B81BCFA004AAD3C /* AppCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorImpl.swift; sourceTree = ""; }; + 0989043D2B81BD50004AAD3C /* CoordinatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoordinatorFactory.swift; sourceTree = ""; }; + 0989043F2B81BFAF004AAD3C /* ViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerFactory.swift; sourceTree = ""; }; + 098904432B81C18A004AAD3C /* HomeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCoordinator.swift; sourceTree = ""; }; + 098904452B81C191004AAD3C /* MypageCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageCoordinator.swift; sourceTree = ""; }; + 098904472B81C197004AAD3C /* AchieveCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveCoordinator.swift; sourceTree = ""; }; + 0989044B2B81C210004AAD3C /* HomecoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomecoordinatorImpl.swift; sourceTree = ""; }; + 0989044D2B81C216004AAD3C /* MypageCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageCoordinatorImpl.swift; sourceTree = ""; }; + 0989044F2B81C21D004AAD3C /* AchieveCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveCoordinatorImpl.swift; sourceTree = ""; }; + 098904512B81CA33004AAD3C /* UpdateCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCoordinatorImpl.swift; sourceTree = ""; }; + 098904532B81CA47004AAD3C /* UpdateCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCoordinator.swift; sourceTree = ""; }; + 098A23A12B833C6700265955 /* AuthCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthCoordinator.swift; sourceTree = ""; }; + 098A23A32B833F0300265955 /* AuthCoordinatorImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthCoordinatorImpl.swift; sourceTree = ""; }; 098BFD5829B7999E008E80F9 /* MyProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileCollectionViewCell.swift; sourceTree = ""; }; 098BFD5A29B79B6A008E80F9 /* MyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoModel.swift; sourceTree = ""; }; 098BFD5C29B79CE3008E80F9 /* InfoCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoCollectionViewCell.swift; sourceTree = ""; }; @@ -211,9 +251,20 @@ 099FC98229B30A2E005B37E6 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 099FC98829B3233D005B37E6 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAnotherDayResponseDTO.swift; sourceTree = ""; }; - 09A8E48B2B9DBE5C00C0F48F /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = API_KEY.plist; path = ../../../../Desktop/API_KEY.plist; sourceTree = ""; }; 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAPI.swift; sourceTree = ""; }; 09CF56032B09F23800526C8C /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = ""; }; + 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModel.swift; sourceTree = ""; }; + 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; + 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModelImpl.swift; sourceTree = ""; }; + 09DCB8552BA0308D00B6BB74 /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = ""; }; + 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorReponse.swift; sourceTree = ""; }; + 09DCB8592BA030DB00B6BB74 /* BaseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseService.swift; sourceTree = ""; }; + 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveManager.swift; sourceTree = ""; }; + 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveManagerImpl.swift; sourceTree = ""; }; + 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementModel.swift; sourceTree = ""; }; + 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementViewModel.swift; sourceTree = ""; }; + 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementModel.swift; sourceTree = ""; }; + 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementViewModelImpl.swift; sourceTree = ""; }; 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyMissionResponseDTO.swift; sourceTree = ""; }; 09DCCD222A18EFB0003DCF8A /* MissionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionAPI.swift; sourceTree = ""; }; 09ED941A2B2ABAB7001864EF /* CommonNotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonNotificationViewController.swift; sourceTree = ""; }; @@ -314,9 +365,6 @@ 6CF4705A29A68EA9008D145C /* URLConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLConstant.swift; sourceTree = ""; }; 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkLoggerPlugin.swift; sourceTree = ""; }; 6CF4705E29A69025008D145C /* GeneralResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralResponse.swift; sourceTree = ""; }; - 6CF4706029A69096008D145C /* NetworkConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConstant.swift; sourceTree = ""; }; - 6CF4706229A690CD008D145C /* NetworkResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkResult.swift; sourceTree = ""; }; - 6CF4706429A690E5008D145C /* NetworkBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBase.swift; sourceTree = ""; }; 6CF4706929A71D71008D145C /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 6CF4706C29A739D9008D145C /* RecommendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendViewController.swift; sourceTree = ""; }; 6CF4706F29A73A15008D145C /* RecommendActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionViewController.swift; sourceTree = ""; }; @@ -384,7 +432,7 @@ children = ( 0921611A2A5727EF0019CC8C /* AnalyticsEventProtocol.swift */, 0921611C2A57D0920019CC8C /* AmplitudeAnalyticsService.swift */, - 095FEE172B9ED30A00FF44C0 /* ViewModel.swift */, + 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */, ); path = Protocol; sourceTree = ""; @@ -415,6 +463,7 @@ 093DB0312A1468F100ECA5F6 /* Service */ = { isa = PBXGroup; children = ( + 09DCB8592BA030DB00B6BB74 /* BaseService.swift */, 0987C83E2B9DD234007EE8DE /* MissionService */, 3B892AB32A2FB67B00A316BC /* Auth */, 3B892AB02A2FB63E00A316BC /* Recommend */, @@ -446,7 +495,10 @@ 095FEE142B9ED27F00FF44C0 /* ViewModel */ = { isa = PBXGroup; children = ( - 095FEE152B9ED28800FF44C0 /* DetailAchievementViewModel.swift */, + 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */, + 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */, + 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */, + 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */, ); path = ViewModel; sourceTree = ""; @@ -459,6 +511,70 @@ name = MissionService; sourceTree = ""; }; + 0989042D2B81BB22004AAD3C /* Coordinator */ = { + isa = PBXGroup; + children = ( + 0989043C2B81BD44004AAD3C /* Factory */, + 098904362B81BC03004AAD3C /* Protocol */, + 098904392B81BCF2004AAD3C /* Coordinator */, + 0989042E2B81BB34004AAD3C /* Common */, + 098904332B81BB74004AAD3C /* Type */, + ); + path = Coordinator; + sourceTree = ""; + }; + 0989042E2B81BB34004AAD3C /* Common */ = { + isa = PBXGroup; + children = ( + 0989042F2B81BB3A004AAD3C /* Coordinator.swift */, + 098904312B81BB43004AAD3C /* CoordinatorDelegate.swift */, + ); + path = Common; + sourceTree = ""; + }; + 098904332B81BB74004AAD3C /* Type */ = { + isa = PBXGroup; + children = ( + 098904342B81BB85004AAD3C /* CoordinatorType.swift */, + ); + path = Type; + sourceTree = ""; + }; + 098904362B81BC03004AAD3C /* Protocol */ = { + isa = PBXGroup; + children = ( + 098904372B81BC16004AAD3C /* AppCoordinator.swift */, + 098904532B81CA47004AAD3C /* UpdateCoordinator.swift */, + 098A23A12B833C6700265955 /* AuthCoordinator.swift */, + 098904432B81C18A004AAD3C /* HomeCoordinator.swift */, + 098904452B81C191004AAD3C /* MypageCoordinator.swift */, + 098904472B81C197004AAD3C /* AchieveCoordinator.swift */, + ); + path = Protocol; + sourceTree = ""; + }; + 098904392B81BCF2004AAD3C /* Coordinator */ = { + isa = PBXGroup; + children = ( + 0989043A2B81BCFA004AAD3C /* AppCoordinatorImpl.swift */, + 098904512B81CA33004AAD3C /* UpdateCoordinatorImpl.swift */, + 098A23A32B833F0300265955 /* AuthCoordinatorImpl.swift */, + 0989044B2B81C210004AAD3C /* HomecoordinatorImpl.swift */, + 0989044F2B81C21D004AAD3C /* AchieveCoordinatorImpl.swift */, + 0989044D2B81C216004AAD3C /* MypageCoordinatorImpl.swift */, + ); + path = Coordinator; + sourceTree = ""; + }; + 0989043C2B81BD44004AAD3C /* Factory */ = { + isa = PBXGroup; + children = ( + 0989043D2B81BD50004AAD3C /* CoordinatorFactory.swift */, + 0989043F2B81BFAF004AAD3C /* ViewControllerFactory.swift */, + ); + path = Factory; + sourceTree = ""; + }; 098BFD6029B80137008E80F9 /* Cell */ = { isa = PBXGroup; children = ( @@ -535,6 +651,32 @@ path = Mission; sourceTree = ""; }; + 09DCB85B2BA0316500B6BB74 /* Manager */ = { + isa = PBXGroup; + children = ( + 09DCB85C2BA0316A00B6BB74 /* ManagerInterface */, + 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */, + ); + path = Manager; + sourceTree = ""; + }; + 09DCB85C2BA0316A00B6BB74 /* ManagerInterface */ = { + isa = PBXGroup; + children = ( + 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */, + ); + path = ManagerInterface; + sourceTree = ""; + }; + 09DCB8632BA056BC00B6BB74 /* Model */ = { + isa = PBXGroup; + children = ( + 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */, + 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */, + ); + path = Model; + sourceTree = ""; + }; 09F6717E29CAD68100708725 /* Onboarding */ = { isa = PBXGroup; children = ( @@ -650,6 +792,7 @@ isa = PBXGroup; children = ( 6CF4705829A60A79008D145C /* iOS-NOTTODO.entitlements */, + 0989042D2B81BB22004AAD3C /* Coordinator */, 3B027A8B299C335E00BEB65C /* Application */, 3B027A8C299C337900BEB65C /* Global */, 3B027A90299C33CA00BEB65C /* Network */, @@ -716,6 +859,7 @@ 3B027A90299C33CA00BEB65C /* Network */ = { isa = PBXGroup; children = ( + 09DCB85B2BA0316500B6BB74 /* Manager */, 155E45672B5FF2DE008628E7 /* External */, 093DB0312A1468F100ECA5F6 /* Service */, 093DB02F2A1468CE00ECA5F6 /* DataModel */, @@ -776,6 +920,7 @@ 3B027A9B299C348800BEB65C /* Achievement */ = { isa = PBXGroup; children = ( + 09DCB8632BA056BC00B6BB74 /* Model */, 095FEE142B9ED27F00FF44C0 /* ViewModel */, 095FEE0F2B9ED15600FF44C0 /* Views */, 09F6719929CC8ECD00708725 /* ViewControllers */, @@ -808,7 +953,7 @@ 3B027AA6299C359900BEB65C /* Resource */ = { isa = PBXGroup; children = ( - 09A8E48B2B9DBE5C00C0F48F /* API_KEY.plist */, + 098904282B81AF3E004AAD3C /* API_KEY.plist */, 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */, 3B027A85299C31B600BEB65C /* Info.plist */, 3B027AAA299C35D000BEB65C /* Assets */, @@ -1093,9 +1238,8 @@ 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */, 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */, 6CF4705E29A69025008D145C /* GeneralResponse.swift */, - 6CF4706029A69096008D145C /* NetworkConstant.swift */, - 6CF4706229A690CD008D145C /* NetworkResult.swift */, - 6CF4706429A690E5008D145C /* NetworkBase.swift */, + 09DCB8552BA0308D00B6BB74 /* APIError.swift */, + 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */, ); path = Base; sourceTree = ""; @@ -1254,7 +1398,7 @@ 3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */, 3B027A84299C31B600BEB65C /* LaunchScreen.storyboard in Resources */, 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */, - 09A8E48C2B9DBE5C00C0F48F /* API_KEY.plist in Resources */, + 098904292B81AF3E004AAD3C /* API_KEY.plist in Resources */, 3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */, 6C049A312A595C670085E40B /* logo.mp4 in Resources */, 3B027A81299C31B600BEB65C /* Assets.xcassets in Resources */, @@ -1296,28 +1440,36 @@ 09F6719729CC81B500708725 /* DetailAchievementCollectionViewCell.swift in Sources */, 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */, 3B14A14129A6FDA900F92897 /* UILabel+.swift in Sources */, + 09DCB8672BA05F5400B6BB74 /* AchievementViewModel.swift in Sources */, 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */, 3B027A7C299C31B500BEB65C /* AuthViewController.swift in Sources */, + 098904382B81BC16004AAD3C /* AppCoordinator.swift in Sources */, 6C16016229C59EFD005AE3F5 /* MyInfoAccountModel.swift in Sources */, 3B027A92299C33FE00BEB65C /* UIColor+.swift in Sources */, 092C09B52A484DD900E9B06B /* HomeDeleteViewController.swift in Sources */, 3B027AA4299C357000BEB65C /* MyInfoViewController.swift in Sources */, + 0989043B2B81BCFA004AAD3C /* AppCoordinatorImpl.swift in Sources */, 097568362A2FEF630001EC46 /* String+.swift in Sources */, 6CA2B7BB2A222D2300A9E549 /* ValueOnboardingViewController.swift in Sources */, 3B5F8F8129BF90190063A7F8 /* NottodoCollectionViewCell.swift in Sources */, 3B027A94299C340600BEB65C /* UIFont+.swift in Sources */, 6CA208292A191185001C4247 /* UIImageView+.swift in Sources */, 0982DE5A29AE5E6000D933D2 /* CompositionalLayout.swift in Sources */, + 098904522B81CA33004AAD3C /* UpdateCoordinatorImpl.swift in Sources */, + 098904442B81C18A004AAD3C /* HomeCoordinator.swift in Sources */, 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */, 3B482FA3299EA9CB00BCF424 /* TabBarItem.swift in Sources */, 3B027AA0299C353700BEB65C /* AddMissionViewController.swift in Sources */, 098BFD5929B7999E008E80F9 /* MyProfileCollectionViewCell.swift in Sources */, + 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */, 3B5F8F7A29BF8E8D0063A7F8 /* AddMissionProtocol.swift in Sources */, + 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */, 09F6718C29CB4AB700708725 /* SubOnboardingCollectionViewCell.swift in Sources */, 0921611D2A57D0920019CC8C /* AmplitudeAnalyticsService.swift in Sources */, 3B027A96299C340C00BEB65C /* UIImage+.swift in Sources */, 0921611F2A57D7BF0019CC8C /* AnalyticsEvent.swift in Sources */, 098BFD5B29B79B6A008E80F9 /* MyInfoModel.swift in Sources */, + 09DCB85A2BA030DB00B6BB74 /* BaseService.swift in Sources */, 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */, 6CF4707A29A7AAFF008D145C /* PaddingLabel.swift in Sources */, 6CA208272A18FFCF001C4247 /* RecommendResponseDTO.swift in Sources */, @@ -1325,7 +1477,9 @@ 3B027A78299C31B500BEB65C /* AppDelegate.swift in Sources */, 3B50CB212A40E75400F2E761 /* AddMissionResponseDTO.swift in Sources */, 3B37AE2929C8821600AB7587 /* GoalCollectionViewCell.swift in Sources */, + 098904322B81BB43004AAD3C /* CoordinatorDelegate.swift in Sources */, 3B14A14329A6FEE400F92897 /* UITextField+.swift in Sources */, + 098904482B81C197004AAD3C /* AchieveCoordinator.swift in Sources */, 09022D4629C44BC300DE6E49 /* MissionCalendarCell.swift in Sources */, 6CA2083A2A195906001C4247 /* AuthResponseDTO.swift in Sources */, 09582B4B29BDE37C00EF3207 /* DetailFooterReusableView.swift in Sources */, @@ -1341,6 +1495,7 @@ 3B027A9E299C34DA00BEB65C /* HomeViewController.swift in Sources */, 09F6719029CB6AB400708725 /* OnboardingFooterView.swift in Sources */, 3B482FA9299EB95400BCF424 /* UIScreen+.swift in Sources */, + 098904352B81BB85004AAD3C /* CoordinatorType.swift in Sources */, 099FC98329B30A2E005B37E6 /* Utils.swift in Sources */, 3B892ABB2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift in Sources */, 3B14A13B29A694C000F92897 /* UITextView+.swift in Sources */, @@ -1350,12 +1505,16 @@ 3B482FA7299EB8FD00BCF424 /* UIViewController+.swift in Sources */, 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */, 3B03D0D82B0F5EF300302872 /* CGSize+.swift in Sources */, + 098904502B81C21D004AAD3C /* AchieveCoordinatorImpl.swift in Sources */, 6CA208302A1925EE001C4247 /* RecommendActionResponseDTO.swift in Sources */, + 098A23A22B833C6700265955 /* AuthCoordinator.swift in Sources */, 09F6718629CB26E400708725 /* OnboardingHeaderView.swift in Sources */, 3B11740D2A4B574B0033DDF3 /* CALayer+.swift in Sources */, 155E456D2B62B1A1008628E7 /* UpdateCheckViewController.swift in Sources */, + 098904302B81BB3A004AAD3C /* Coordinator.swift in Sources */, 3B14A13D29A6FBD300F92897 /* UIView+.swift in Sources */, 09F6719529CBFCD200708725 /* GradientView.swift in Sources */, + 09DCB8622BA031F600B6BB74 /* AchieveManagerImpl.swift in Sources */, 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */, 6CA208252A18FEEA001C4247 /* RecommendService.swift in Sources */, 3B482FA5299EAB8800BCF424 /* TabBarController.swift in Sources */, @@ -1366,18 +1525,23 @@ 09F6718429CADB1100708725 /* OnboardingModel.swift in Sources */, 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */, 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */, + 0989043E2B81BD50004AAD3C /* CoordinatorFactory.swift in Sources */, 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */, 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */, + 09DCB8582BA0309F00B6BB74 /* ErrorReponse.swift in Sources */, 0982DE5829AE40FB00D933D2 /* UITabBar+.swift in Sources */, 3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */, 6C16015829C40112005AE3F5 /* AuthButtonView.swift in Sources */, 3BC19A9329CA1CA800C02803 /* UICollectionViewCell+.swift in Sources */, 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */, + 098904462B81C191004AAD3C /* MypageCoordinator.swift in Sources */, + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */, 3B0CBA242A461B1C0004F2DB /* RecentMissionResponseDTO.swift in Sources */, 3B5F8F7F29BF900A0063A7F8 /* DateCollectionViewCell.swift in Sources */, 3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */, 6CD4F8BA29AA493600CCC740 /* RecommendActionHeaderView.swift in Sources */, 0930DE6229B80550007958DE /* MissionDetailViewController.swift in Sources */, + 0989044C2B81C210004AAD3C /* HomecoordinatorImpl.swift in Sources */, 3B14A13F29A6FCB300F92897 /* UIStackView+.swift in Sources */, 6CD4F8BC29AA494300CCC740 /* RecommendActionFooterView.swift in Sources */, 6CD4F8BE29AA495900CCC740 /* RecommendActionCollectionViewCell.swift in Sources */, @@ -1388,13 +1552,10 @@ 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */, 097C003629AB8270008CAEF3 /* MissionListCollectionViewCell.swift in Sources */, 6C16015C29C56DBA005AE3F5 /* MyInfoAccountViewController.swift in Sources */, - 6CF4706129A69096008D145C /* NetworkConstant.swift in Sources */, 09F6718E29CB612B00708725 /* FifthOnboardingViewController.swift in Sources */, 0987C8402B9DD4DC007EE8DE /* MissionService.swift in Sources */, 6CD4F8C229AA5AF200CCC740 /* UIButton+.swift in Sources */, 6CA208362A1957CA001C4247 /* AuthAPI.swift in Sources */, - 095FEE162B9ED28800FF44C0 /* DetailAchievementViewModel.swift in Sources */, - 6CF4706529A690E5008D145C /* NetworkBase.swift in Sources */, 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */, 6C16016429C5E37D005AE3F5 /* MyInfoAccountStackView.swift in Sources */, 3B80B5D52B7F304D00697250 /* adjust+.swift in Sources */, @@ -1402,28 +1563,36 @@ 6CF4707029A73A15008D145C /* RecommendActionViewController.swift in Sources */, 0943A9F92A53239200614761 /* Bundle+.swift in Sources */, 0982DE5429ADCCE000D933D2 /* HomeEmptyCollectionViewCell.swift in Sources */, + 09DCB86B2BA0600600B6BB74 /* AchievementViewModelImpl.swift in Sources */, 3BEEBE972A4B048A0081C936 /* NottodoToastView.swift in Sources */, 3B027AA2299C355800BEB65C /* AchievementViewController.swift in Sources */, 3B5F8F8929BF9EFE0063A7F8 /* AddMissionLabel.swift in Sources */, 09582B4F29BEBAFA00EF3207 /* DetailCalendarViewController.swift in Sources */, 09F6718829CB383800708725 /* ThirdOnboardingViewController.swift in Sources */, 3B37AE2B29C8904800AB7587 /* RecommendKeywordCollectionViewCell.swift in Sources */, + 09DCB84F2BA0147500B6BB74 /* ViewModel.swift in Sources */, 099FC98929B3233D005B37E6 /* CalendarView.swift in Sources */, 09ED941B2B2ABAB7001864EF /* CommonNotificationViewController.swift in Sources */, 6C9628A92A22209E003ADE25 /* LogoOnboardingViewController.swift in Sources */, 09F6718029CAD76C00708725 /* SecondOnboardingViewController.swift in Sources */, + 098A23A42B833F0300265955 /* AuthCoordinatorImpl.swift in Sources */, 098BFD5D29B79CE3008E80F9 /* InfoCollectionViewCell.swift in Sources */, 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */, 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */, 155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */, - 095FEE182B9ED30A00FF44C0 /* ViewModel.swift in Sources */, - 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */, + 09DCB8562BA0308D00B6BB74 /* APIError.swift in Sources */, + 098904402B81BFAF004AAD3C /* ViewControllerFactory.swift in Sources */, 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */, 3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */, 3B4E12F62A27C0BE001D1EC1 /* QuitModalView.swift in Sources */, 6CA208232A18FE78001C4247 /* RecommendAPI.swift in Sources */, 6CA208342A1956ED001C4247 /* AuthService.swift in Sources */, 0921611B2A5727EF0019CC8C /* AnalyticsEventProtocol.swift in Sources */, + 09DCB8692BA05F9E00B6BB74 /* AchievementModel.swift in Sources */, + 098904542B81CA47004AAD3C /* UpdateCoordinator.swift in Sources */, + 0989044E2B81C216004AAD3C /* MypageCoordinatorImpl.swift in Sources */, + 09DCB8652BA056C800B6BB74 /* DetailAchievementModel.swift in Sources */, + 09DCB8602BA031E000B6BB74 /* AchieveManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift index e36d0316..92252abf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift @@ -36,7 +36,7 @@ final class AchieveCoordinatorImpl: AchieveCoordinator { navigationController.setViewControllers([viewController], animated: false) } - func showAchieveDetailViewController(selectedDate: Date) { + func showAchieveDetailViewController(selectedDate: String) { let viewController = viewControllerFactory.makeAchieveDetailViewController(coordinator: self, date: selectedDate) viewController.modalTransitionStyle = .crossDissolve viewController.modalPresentationStyle = .overFullScreen diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift index 13e88600..7c66abb9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift @@ -45,7 +45,7 @@ protocol MyPageFlowControllerFactory { protocol AchieveFlowControllerFactory { func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController } protocol TabBarControllerFactory { @@ -211,7 +211,7 @@ extension ViewControllerFactoryImpl { return viewController } - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController { + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController { let missionAPI = DefaultMissionAPI() let manager = AchieveManagerImpl(missionAPI: missionAPI) let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, manager: manager) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift index cd5ca4c9..1782522a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift @@ -9,5 +9,5 @@ import Foundation protocol AchieveCoordinator: Coordinator { func showAchieveViewController() - func showAchieveDetailViewController(selectedDate: Date) + func showAchieveDetailViewController(selectedDate: String) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift index 54c768aa..2c81b2ae 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -25,18 +25,19 @@ final class AchieveManagerImpl: AchieveManager { .eraseToAnyPublisher() } - func getAchieveCalendar(month: String) -> AnyPublisher { - return missionAPI.getAchieveCalendar(month: month) + func getAchieveCalendar(month: Date) -> AnyPublisher { + return missionAPI.getAchieveCalendar(month: month.formattedString(format: "yyyy-MM")) .map { response in - self.convertResponseToCalendarEventData(response) + + self.convertResponseToCalendarEventData(response, month: month) } .compactMap { $0 } .eraseToAnyPublisher() } - private func convertResponseToCalendarEventData(_ response: CalendarData) -> CalendarEventData? { + private func convertResponseToCalendarEventData(_ response: CalendarData, month: Date) -> CalendarEventData? { guard let data = response.data else { return nil } let calendarData = Dictionary(uniqueKeysWithValues: data.map { ($0.actionDate, $0.percentage) }) - return CalendarEventData(percentages: calendarData) + return CalendarEventData(month: month, percentages: calendarData) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift index 16c32aca..65aa70e5 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift @@ -10,5 +10,5 @@ import Combine protocol AchieveManager { func getDailyMission(date: String) -> AnyPublisher - func getAchieveCalendar(month: String) -> AnyPublisher + func getAchieveCalendar(month: Date) -> AnyPublisher } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift index 94934409..2105b07a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift @@ -8,6 +8,6 @@ import Foundation struct CalendarEventData { - + let month: Date let percentages: [String: Float] } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 732f2f3d..8777ce10 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -12,16 +12,16 @@ import FSCalendar import Then import SnapKit -final class AchievementViewController: UIViewController { +final class AchievementViewController: UIViewController, AchievementViewModelPresentable { // MARK: - Properties - private let viewWillAppearSubject = PassthroughSubject() + private let viewWillAppearSubject = PassthroughSubject() private let eventCellSubject = PassthroughSubject() private let monthSubject = PassthroughSubject() + private var dataSource: [String: Float] = [:] private weak var coordinator: AchieveCoordinator? - private var dataSource: [String: Float] = [:] private lazy var safeArea = self.view.safeAreaLayoutGuide @@ -51,10 +51,7 @@ final class AchievementViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - viewWillAppearSubject.send(()) - monthSubject.send(Date()) - monthCalendar.currentPage(date: Date()) - monthCalendar.configureYearMonth(to: Date().formattedString(format: I18N.yearMonthTitle)) + viewWillAppearSubject.send(Date()) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) } @@ -132,6 +129,8 @@ extension AchievementViewController { .sink { [weak self] item in self?.dataSource = item.percentages self?.monthCalendar.reloadCollectionView() + self?.monthCalendar.currentPage(date: item.month) + self?.monthCalendar.configureYearMonth(to: item.month.formattedString(format: I18N.yearMonthTitle)) } .store(in: &cancelBag) } @@ -152,9 +151,7 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F calendar.appearance.selectionColor = .clear calendar.appearance.titleSelectionColor = .white - if self.dataSource.contains(where: { $0.key == date.formattedString() }) { - self.eventCellSubject.send(date) - } + self.eventCellSubject.send(date) } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { @@ -174,9 +171,10 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F } return percentage == 1.0 ? .black : .white } - + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell + + guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } guard let percentage = self.dataSource[date.formattedString()] else { return cell } cell.configure(percent: percentage) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift index 22b295ac..b9c4c1c3 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift @@ -12,7 +12,7 @@ protocol AchievementViewModelPresentable {} protocol AchievementViewModel: ViewModel where Input == AchievementViewModelInput, Output == AchievementViewModelOutput {} struct AchievementViewModelInput { - let viewWillAppearSubject: PassthroughSubject + let viewWillAppearSubject: PassthroughSubject let calendarCellTapped: PassthroughSubject let currentMonthSubject: PassthroughSubject } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift index 540bf2fd..aa6909a6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift @@ -8,11 +8,10 @@ import Foundation import Combine -final class AchievementViewModelImpl: AchievementViewModel, AchievementViewModelPresentable { +final class AchievementViewModelImpl: AchievementViewModel { private weak var coordinator: AchieveCoordinator? private var manager: AchieveManager - private var month: String? private var cancelBag = Set() init(coordinator: AchieveCoordinator, manager: AchieveManager) { @@ -21,36 +20,41 @@ final class AchievementViewModelImpl: AchievementViewModel, AchievementViewModel } let eventSubject = PassthroughSubject() + var dataSource: [String: Float] = [:] func transform(input: AchievementViewModelInput) -> AchievementViewModelOutput { - Publishers.CombineLatest(input.viewWillAppearSubject, input.currentMonthSubject) - .map { _, month in month } + input.viewWillAppearSubject + .merge(with: input.currentMonthSubject) .removeDuplicates() .sink { [weak self] month in guard let self = self else { return } - let monthString = month.formattedString(format: "yyyy-MM") - self.month = monthString - self.getCalendarEvent(month: monthString) + self.getCalendarEvent(month: month) } .store(in: &cancelBag) input.calendarCellTapped + .compactMap { date -> String? in + guard let percentage = self.dataSource[date.formattedString()], percentage != 0.0 else { + return nil + } + return date.formattedString() + } .sink { [weak self] date in self?.coordinator?.showAchieveDetailViewController(selectedDate: date) } - .store(in: &cancelBag) + .store(in: &cancelBag) return Output(viewWillAppearSubject: eventSubject.eraseToAnyPublisher()) } - func getCalendarEvent(month: String) { + func getCalendarEvent(month: Date) { manager.getAchieveCalendar(month: month) .sink(receiveCompletion: { event in print("completion: \(event)") }, receiveValue: { data in - dump(data) self.eventSubject.send(data) + self.dataSource = data.percentages }) .store(in: &cancelBag) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift index 9efaf74a..59b6f828 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift @@ -8,7 +8,9 @@ import Foundation import Combine -protocol DetailAchievementViewModelPresentable {} +protocol DetailAchievementViewModelPresentable { + func selectedDate(_ date: String) +} protocol DetailAchievementViewModel: ViewModel where Input == DetailAchievementViewModelInput, Output == DetailAchievementViewModelOutput {} struct DetailAchievementViewModelInput { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift index 45abc435..8b826666 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift @@ -8,7 +8,7 @@ import Foundation import Combine -final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAchievementViewModelPresentable { +final class DetailAchievementViewModelImpl: DetailAchievementViewModel { private weak var coordinator: AchieveCoordinator? private var manager: AchieveManager @@ -50,8 +50,10 @@ final class DetailAchievementViewModelImpl: DetailAchievementViewModel, DetailAc }) .store(in: &cancelBag) } - - func selectedDate(_ date: Date) { - self.selectedDate = date.formattedString(format: "YYYY-MM-dd") +} + +extension DetailAchievementViewModelImpl: DetailAchievementViewModelPresentable { + func selectedDate(_ date: String) { + self.selectedDate = date } } From 34fd5f14905169fa17ea84d8dd2a1bd59cf8cb41 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 13 Mar 2024 15:17:33 +0900 Subject: [PATCH 15/21] =?UTF-8?q?[Fix]=20#241=20-=20=EC=84=B1=EC=B7=A8=20?= =?UTF-8?q?=EB=94=94=ED=85=8C=EC=9D=BC=20UI=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataModel/Home/DailyMissionResponseDTO.swift | 10 ++++++++++ .../Network/Manager/AchieveManagerImpl.swift | 10 ++++------ .../Manager/ManagerInterface/AchieveManager.swift | 2 +- .../Cell/DetailAchievementCollectionViewCell.swift | 6 +++--- .../Achievement/Model/DetailAchievementModel.swift | 4 +++- .../DetailAchievementViewController.swift | 4 +++- .../ViewModel/DetailAchievementViewModelImpl.swift | 2 +- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift index 01d2cb30..e8764274 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift @@ -29,3 +29,13 @@ struct DailyMissionResponseDTO: Codable, Hashable { lhs.uuid == rhs.uuid } } + +extension DailyMissionResponseDTO { + + func toData(selectedDate: String) -> AchieveDetailData { + return AchieveDetailData(title: self.title, + situation: self.situationName, + status: self.completionStatus, + selectedDate: selectedDate) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift index 2c81b2ae..112cd3cc 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -16,19 +16,17 @@ final class AchieveManagerImpl: AchieveManager { init(missionAPI: DefaultMissionAPI) { self.missionAPI = missionAPI } - - func getDailyMission(date: String) -> AnyPublisher { + + func getDailyMission(date: String) -> AnyPublisher<[AchieveDetailData], Error> { missionAPI.getDailyMission(date: date) - .map { data -> AchieveDetailData in - return AchieveDetailData(missionList: data.data ?? [], selectedDate: date) - } + .map { $0.data?.map { $0.toData(selectedDate: date) } ?? [] } .eraseToAnyPublisher() } func getAchieveCalendar(month: Date) -> AnyPublisher { return missionAPI.getAchieveCalendar(month: month.formattedString(format: "yyyy-MM")) + // .map { $0.data?.map { $0.toData(month: month) } ?? [} .map { response in - self.convertResponseToCalendarEventData(response, month: month) } .compactMap { $0 } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift index 65aa70e5..ae72ad78 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift @@ -9,6 +9,6 @@ import Foundation import Combine protocol AchieveManager { - func getDailyMission(date: String) -> AnyPublisher + func getDailyMission(date: String) -> AnyPublisher<[AchieveDetailData], Error> func getAchieveCalendar(month: Date) -> AnyPublisher } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift index 4cd928df..a1ca442f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift @@ -85,10 +85,10 @@ extension DetailAchievementCollectionViewCell { } } - func configure(model: DailyMissionResponseDTO) { - tagLabel.text = model.situationName + func configure(model: AchieveDetailData) { + tagLabel.text = model.situation titleLabel.text = model.title titleLabel.lineBreakMode = .byTruncatingTail - checkImage.isHidden = model.completionStatus == .UNCHECKED + checkImage.isHidden = model.status == .UNCHECKED } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift index 26bdd532..e6518698 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift @@ -9,7 +9,9 @@ import Foundation struct AchieveDetailData: Hashable { - let missionList: [DailyMissionResponseDTO] + let title: String + let situation: String + let status: CompletionStatus let selectedDate: String func formatDateString() -> String { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 485c5e72..d9700d39 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -107,7 +107,8 @@ extension DetailAchievementViewController { private func setDataSource() { let cellRegistration = CellRegistration {cell, index, item in - cell.configure(model: item.missionList[index.item]) + print("index: \(index.item)") + cell.configure(model: item) } let headerRegistration = HeaderRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, _, indexPath in @@ -144,6 +145,7 @@ extension DetailAchievementViewController { private func setSnapShot(with items: [AchieveDetailData]) { var snapShot = SnapShot() + snapShot.appendSections([.main]) snapShot.appendItems(items, toSection: .main) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift index 8b826666..6944a6c9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift @@ -46,7 +46,7 @@ final class DetailAchievementViewModelImpl: DetailAchievementViewModel { .sink(receiveCompletion: { event in print("completion: \(event)") }, receiveValue: { data in - self.missionsSubject.send([data]) + self.missionsSubject.send(data) }) .store(in: &cancelBag) } From 11e3b67804b2972f8a2f61304ae726e03bbbd320 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 13 Mar 2024 16:26:58 +0900 Subject: [PATCH 16/21] =?UTF-8?q?[Fix]=20#241=20-=20=EC=84=B1=EC=B7=A8?= =?UTF-8?q?=EB=B7=B0=20=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 --- .../Achieve/CalendarReponseDTO.swift | 24 ++++++++++----- .../Network/Manager/AchieveManagerImpl.swift | 16 +++------- .../AchievementViewController.swift | 29 ++++++++++--------- .../ViewModel/AchievementViewModelImpl.swift | 15 +++++----- iOS-NOTTODO/iOS-NOTTODO/Resource/Info.plist | 10 +++---- 5 files changed, 48 insertions(+), 46 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift index 67b49b1b..ccb86e5d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift @@ -12,18 +12,26 @@ struct CalendarReponseDTO: Codable { let percentage: Float func toDate(dateString: String) -> Date? { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy.MM.dd" - dateFormatter.timeZone = TimeZone(identifier: "UTC") - if let date = dateFormatter.date(from: dateString) { - return date - } else { - return nil - } + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy.MM.dd" + dateFormatter.timeZone = TimeZone(identifier: "UTC") + if let date = dateFormatter.date(from: dateString) { + return date + } else { + return nil } + } func convert() -> [Date: Float] { guard let date = self.toDate(dateString: actionDate) else { return [:]} return [date: percentage] } } + +extension Array where Element == CalendarReponseDTO { + + func toData(month: Date) -> CalendarEventData { + let calendarData = Dictionary(uniqueKeysWithValues: self.map { ($0.actionDate, $0.percentage) }) + return CalendarEventData(month: month, percentages: calendarData) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift index 112cd3cc..589e953b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -16,26 +16,18 @@ final class AchieveManagerImpl: AchieveManager { init(missionAPI: DefaultMissionAPI) { self.missionAPI = missionAPI } - + func getDailyMission(date: String) -> AnyPublisher<[AchieveDetailData], Error> { missionAPI.getDailyMission(date: date) - .map { $0.data?.map { $0.toData(selectedDate: date) } ?? [] } + .map { $0.data?.map { $0.toData(selectedDate: date) } } + .compactMap { $0 } .eraseToAnyPublisher() } func getAchieveCalendar(month: Date) -> AnyPublisher { return missionAPI.getAchieveCalendar(month: month.formattedString(format: "yyyy-MM")) - // .map { $0.data?.map { $0.toData(month: month) } ?? [} - .map { response in - self.convertResponseToCalendarEventData(response, month: month) - } + .map { $0.data?.toData(month: month) } .compactMap { $0 } .eraseToAnyPublisher() } - - private func convertResponseToCalendarEventData(_ response: CalendarData, month: Date) -> CalendarEventData? { - guard let data = response.data else { return nil } - let calendarData = Dictionary(uniqueKeysWithValues: data.map { ($0.actionDate, $0.percentage) }) - return CalendarEventData(month: month, percentages: calendarData) - } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 8777ce10..0801776e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -19,17 +19,14 @@ final class AchievementViewController: UIViewController, AchievementViewModelPre private let viewWillAppearSubject = PassthroughSubject() private let eventCellSubject = PassthroughSubject() private let monthSubject = PassthroughSubject() - private var dataSource: [String: Float] = [:] - - private weak var coordinator: AchieveCoordinator? - - private lazy var safeArea = self.view.safeAreaLayoutGuide - + private let dataSource = CurrentValueSubject<[String: Float], Never>([:]) + private let viewModel: any AchievementViewModel private var cancelBag = Set() // MARK: - UI Components + private lazy var safeArea = self.view.safeAreaLayoutGuide private let scrollView = UIScrollView() private let achievementLabel = UILabel() private let monthCalendar = CalendarView(scope: .month) @@ -127,13 +124,17 @@ extension AchievementViewController { output.viewWillAppearSubject .receive(on: RunLoop.main) .sink { [weak self] item in - self?.dataSource = item.percentages - self?.monthCalendar.reloadCollectionView() - self?.monthCalendar.currentPage(date: item.month) - self?.monthCalendar.configureYearMonth(to: item.month.formattedString(format: I18N.yearMonthTitle)) + self?.updateCalendar(with: item) } .store(in: &cancelBag) } + + func updateCalendar(with data: CalendarEventData) { + self.dataSource.send(data.percentages) + self.monthCalendar.reloadCollectionView() + self.monthCalendar.currentPage(date: data.month) + self.monthCalendar.configureYearMonth(to: data.month.formattedString(format: I18N.yearMonthTitle)) + } } extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { @@ -156,7 +157,7 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { - if let percentage = self.dataSource[date.formattedString()], percentage == 1.0 { + if let percentage = self.dataSource.value[date.formattedString()], percentage == 1.0 { return .black } return .white @@ -166,17 +167,17 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F let currentMonth = Calendar.current.component(.month, from: calendar.currentPage) let dateMonth = Calendar.current.component(.month, from: date) - guard let percentage = self.dataSource[date.formattedString()] else { + guard let percentage = self.dataSource.value[date.formattedString()] else { return currentMonth != dateMonth ? .gray3 : .white } return percentage == 1.0 ? .black : .white } - + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } - guard let percentage = self.dataSource[date.formattedString()] else { return cell } + guard let percentage = self.dataSource.value[date.formattedString()] else { return cell } cell.configure(percent: percentage) return cell diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift index aa6909a6..38244c5b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift @@ -20,7 +20,7 @@ final class AchievementViewModelImpl: AchievementViewModel { } let eventSubject = PassthroughSubject() - var dataSource: [String: Float] = [:] + let dataSource = CurrentValueSubject<[String: Float], Never>([:]) func transform(input: AchievementViewModelInput) -> AchievementViewModelOutput { @@ -34,16 +34,17 @@ final class AchievementViewModelImpl: AchievementViewModel { .store(in: &cancelBag) input.calendarCellTapped - .compactMap { date -> String? in - guard let percentage = self.dataSource[date.formattedString()], percentage != 0.0 else { - return nil + .filter { [weak self] date -> Bool in + guard let percentage = self?.dataSource.value[date.formattedString()] else { + return false } - return date.formattedString() + return percentage != 0.0 } + .map { $0.formattedString() } .sink { [weak self] date in self?.coordinator?.showAchieveDetailViewController(selectedDate: date) } - .store(in: &cancelBag) + .store(in: &cancelBag) return Output(viewWillAppearSubject: eventSubject.eraseToAnyPublisher()) } @@ -54,7 +55,7 @@ final class AchievementViewModelImpl: AchievementViewModel { print("completion: \(event)") }, receiveValue: { data in self.eventSubject.send(data) - self.dataSource = data.percentages + self.dataSource.send(data.percentages) }) .store(in: &cancelBag) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Resource/Info.plist b/iOS-NOTTODO/iOS-NOTTODO/Resource/Info.plist index 53e06a23..488f4b64 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Resource/Info.plist +++ b/iOS-NOTTODO/iOS-NOTTODO/Resource/Info.plist @@ -2,11 +2,6 @@ - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - CFBundleIconName AppIcon CFBundleURLTypes @@ -25,6 +20,11 @@ kakaokompassauth kakaolink + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UIAppFonts Pretendard-Bold.otf From 28579a74a56fc01c81b5aa5fe1e6eac9e5c7e981 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 13 Mar 2024 16:53:28 +0900 Subject: [PATCH 17/21] =?UTF-8?q?[Fix]=20#241=20-=20snapshot=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewControllers/DetailAchievementViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index d9700d39..b956194e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -62,7 +62,7 @@ final class DetailAchievementViewController: UIViewController { setUI() setLayout() setDataSource() - setSnapShot(with: []) + setSnapShot() setBindings() } @@ -143,7 +143,7 @@ extension DetailAchievementViewController { .store(in: &cancelBag) } - private func setSnapShot(with items: [AchieveDetailData]) { + private func setSnapShot(with items: [AchieveDetailData] = []) { var snapShot = SnapShot() snapShot.appendSections([.main]) From 504acbafe50e11c683c392b326686a65173aa7f6 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 13 Mar 2024 17:00:09 +0900 Subject: [PATCH 18/21] =?UTF-8?q?[Fix]=20#241=20-=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AchievementViewController.swift | 36 +++++++++++-------- .../DetailAchievementViewController.swift | 5 +-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 0801776e..0997a85b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -20,10 +20,10 @@ final class AchievementViewController: UIViewController, AchievementViewModelPre private let eventCellSubject = PassthroughSubject() private let monthSubject = PassthroughSubject() private let dataSource = CurrentValueSubject<[String: Float], Never>([:]) - - private let viewModel: any AchievementViewModel private var cancelBag = Set() + private let viewModel: any AchievementViewModel + // MARK: - UI Components private lazy var safeArea = self.view.safeAreaLayoutGuide @@ -137,15 +137,21 @@ extension AchievementViewController { } } -extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { +extension AchievementViewController: FSCalendarDelegate { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { self.monthSubject.send(calendar.currentPage) monthCalendar.configureYearMonth(to: calendar.currentPage.formattedString(format: I18N.yearMonthTitle)) } - func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { - date.formattedString(format: "dd") + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + + guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } + + guard let percentage = self.dataSource.value[date.formattedString()] else { return cell } + cell.configure(percent: percentage) + + return cell } func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { @@ -154,6 +160,16 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F self.eventCellSubject.send(date) } +} + +extension AchievementViewController: FSCalendarDataSource { + + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { + date.formattedString(format: "dd") + } +} + +extension AchievementViewController: FSCalendarDelegateAppearance { func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { @@ -172,14 +188,4 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F } return percentage == 1.0 ? .black : .white } - - func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - - guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } - - guard let percentage = self.dataSource.value[date.formattedString()] else { return cell } - cell.configure(percent: percentage) - - return cell - } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index b956194e..b20cf2c4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -105,9 +105,7 @@ extension DetailAchievementViewController { } private func setDataSource() { - - let cellRegistration = CellRegistration {cell, index, item in - print("index: \(index.item)") + let cellRegistration = CellRegistration {cell, _, item in cell.configure(model: item) } @@ -153,7 +151,6 @@ extension DetailAchievementViewController { } private func layout() -> UICollectionViewCompositionalLayout { - var config = UICollectionLayoutListConfiguration(appearance: .plain) config.headerMode = .supplementary config.backgroundColor = .clear From 598f1af014511da6421584899ca60883bdc7fbc0 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Thu, 14 Mar 2024 13:36:36 +0900 Subject: [PATCH 19/21] =?UTF-8?q?[Fix]=20#241=20-=20Manager=20=EB=A1=9C?= =?UTF-8?q?=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 --- iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift | 4 ++-- .../iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift index bc04dddd..457214e8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Mission/MissionAPI.swift @@ -10,14 +10,14 @@ import Combine import Moya -typealias DefaultMissionAPI = BaseAPI - // 전체 수정 후 - 네이밍 변경 MissionAPI protocol MissionAPIProtocol { func getDailyMission(date: String) -> AnyPublisher func getAchieveCalendar(month: String) -> AnyPublisher } +typealias DefaultMissionAPI = BaseAPI + extension DefaultMissionAPI: MissionAPIProtocol { func getDailyMission(date: String) -> AnyPublisher { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift index 589e953b..055732fc 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -10,10 +10,10 @@ import Combine final class AchieveManagerImpl: AchieveManager { - private let missionAPI: DefaultMissionAPI + private let missionAPI: MissionAPIProtocol private let cancelBag = Set() - init(missionAPI: DefaultMissionAPI) { + init(missionAPI: MissionAPIProtocol) { self.missionAPI = missionAPI } From f10072a30aea2cc0a16dd3b1a8dd8ef9c4ace0b0 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sat, 23 Mar 2024 17:03:13 +0900 Subject: [PATCH 20/21] =?UTF-8?q?[Fix]=20#241=20-=20Factory=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 64 ++++- .../AchieveViewControllerFactory.swift | 38 +++ .../Factory/AuthViewControllerFactory.swift | 52 ++++ .../Factory/HomeViewControllerFactory.swift | 91 +++++++ .../Factory/MyPageViewControllerFactory.swift | 25 ++ .../AchieveFlowControllerFactory.swift | 13 + .../Protocol/AuthFlowControllerFactory.swift | 18 ++ .../Protocol/HomeFlowControllerFactory.swift | 20 ++ .../MyPageFlowControllerFactory.swift | 14 ++ .../TabBarFlowControllerFactory.swift | 12 + .../UpdateFlowcontrollerFactory.swift | 12 + .../Factory/TabBarViewControllerFactory.swift | 24 ++ .../Factory/UpdateViewControllerFactory.swift | 15 ++ .../Factory/ViewControllerFactory.swift | 226 ------------------ .../AchievementViewController.swift | 2 +- 15 files changed, 395 insertions(+), 231 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index ff728d61..f8905f3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -65,6 +65,19 @@ 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56032B09F23800526C8C /* HomeDataSource.swift */; }; 09DB33E02BA2C0DF00B5F961 /* BaseAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DB33DF2BA2C0DF00B5F961 /* BaseAPI.swift */; }; + 09DC0EEE2BAEC1790075AAC9 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */; }; + 09DC0EF12BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */; }; + 09DC0EF32BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */; }; + 09DC0EF52BAEC2030075AAC9 /* HomeFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */; }; + 09DC0EF72BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */; }; + 09DC0EF92BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */; }; + 09DC0EFB2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */; }; + 09DC0EFD2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */; }; + 09DC0EFF2BAEC2750075AAC9 /* HomeViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */; }; + 09DC0F012BAEC27A0075AAC9 /* AuthViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */; }; + 09DC0F032BAEC2800075AAC9 /* MyPageViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */; }; + 09DC0F052BAEC2850075AAC9 /* AchieveViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */; }; + 09DC0F072BAEC2890075AAC9 /* TabBarViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */; }; 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */; }; 09DCB84F2BA0147500B6BB74 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */; }; 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */; }; @@ -137,7 +150,6 @@ 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4E12F72A27C12F001D1EC1 /* WithdrawModalView.swift */; }; 3B4E12FA2A27C4DD001D1EC1 /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B4E12F92A27C4DD001D1EC1 /* Pretendard-Bold.otf */; }; 3B50CB212A40E75400F2E761 /* AddMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B50CB202A40E75400F2E761 /* AddMissionResponseDTO.swift */; }; - 3B570B342BA30E6100418250 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B570B332BA30E6100418250 /* API_KEY.plist */; }; 3B5F8F7A29BF8E8D0063A7F8 /* AddMissionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F7929BF8E8D0063A7F8 /* AddMissionProtocol.swift */; }; 3B5F8F7F29BF900A0063A7F8 /* DateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F7E29BF900A0063A7F8 /* DateCollectionViewCell.swift */; }; 3B5F8F8129BF90190063A7F8 /* NottodoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */; }; @@ -265,6 +277,19 @@ 09A8E48D2B9DBEC700C0F48F /* BaseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseService.swift; sourceTree = ""; }; 09CF56032B09F23800526C8C /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = ""; }; 09DB33DF2BA2C0DF00B5F961 /* BaseAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAPI.swift; sourceTree = ""; }; + 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = API_KEY.plist; path = ../../../../Desktop/API_KEY.plist; sourceTree = ""; }; + 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFlowControllerFactory.swift; sourceTree = ""; }; + 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateFlowcontrollerFactory.swift; sourceTree = ""; }; + 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeFlowControllerFactory.swift; sourceTree = ""; }; + 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageFlowControllerFactory.swift; sourceTree = ""; }; + 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveFlowControllerFactory.swift; sourceTree = ""; }; + 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarFlowControllerFactory.swift; sourceTree = ""; }; + 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateViewControllerFactory.swift; sourceTree = ""; }; + 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewControllerFactory.swift; sourceTree = ""; }; + 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewControllerFactory.swift; sourceTree = ""; }; + 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewControllerFactory.swift; sourceTree = ""; }; + 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveViewControllerFactory.swift; sourceTree = ""; }; + 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewControllerFactory.swift; sourceTree = ""; }; 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModel.swift; sourceTree = ""; }; 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModelImpl.swift; sourceTree = ""; }; @@ -336,7 +361,6 @@ 3B4E12F72A27C12F001D1EC1 /* WithdrawModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawModalView.swift; sourceTree = ""; }; 3B4E12F92A27C4DD001D1EC1 /* Pretendard-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.otf"; sourceTree = ""; }; 3B50CB202A40E75400F2E761 /* AddMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionResponseDTO.swift; sourceTree = ""; }; - 3B570B332BA30E6100418250 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = ""; }; 3B5F8F7929BF8E8D0063A7F8 /* AddMissionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionProtocol.swift; sourceTree = ""; }; 3B5F8F7E29BF900A0063A7F8 /* DateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCollectionViewCell.swift; sourceTree = ""; }; 3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NottodoCollectionViewCell.swift; sourceTree = ""; }; @@ -584,6 +608,13 @@ children = ( 0989043D2B81BD50004AAD3C /* CoordinatorFactory.swift */, 0989043F2B81BFAF004AAD3C /* ViewControllerFactory.swift */, + 09DC0EEF2BAEC1B80075AAC9 /* Protocol */, + 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */, + 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */, + 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */, + 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */, + 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */, + 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */, ); path = Factory; sourceTree = ""; @@ -664,6 +695,19 @@ path = Mission; sourceTree = ""; }; + 09DC0EEF2BAEC1B80075AAC9 /* Protocol */ = { + isa = PBXGroup; + children = ( + 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */, + 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */, + 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */, + 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */, + 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */, + 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */, + ); + path = Protocol; + sourceTree = ""; + }; 09DCB85B2BA0316500B6BB74 /* Manager */ = { isa = PBXGroup; children = ( @@ -968,7 +1012,7 @@ 3B027AA6299C359900BEB65C /* Resource */ = { isa = PBXGroup; children = ( - 3B570B332BA30E6100418250 /* API_KEY.plist */, + 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */, 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */, 3B027A85299C31B600BEB65C /* Info.plist */, 3B027AAA299C35D000BEB65C /* Assets */, @@ -1413,7 +1457,7 @@ 3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */, 3B027A84299C31B600BEB65C /* LaunchScreen.storyboard in Resources */, 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */, - 3B570B342BA30E6100418250 /* API_KEY.plist in Resources */, + 09DC0EEE2BAEC1790075AAC9 /* API_KEY.plist in Resources */, 3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */, 6C049A312A595C670085E40B /* logo.mp4 in Resources */, 3B027A81299C31B600BEB65C /* Assets.xcassets in Resources */, @@ -1456,6 +1500,7 @@ 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */, 3B14A14129A6FDA900F92897 /* UILabel+.swift in Sources */, 09DCB8672BA05F5400B6BB74 /* AchievementViewModel.swift in Sources */, + 09DC0EF92BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift in Sources */, 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */, 3B027A7C299C31B500BEB65C /* AuthViewController.swift in Sources */, 098904382B81BC16004AAD3C /* AppCoordinator.swift in Sources */, @@ -1470,6 +1515,7 @@ 3B027A94299C340600BEB65C /* UIFont+.swift in Sources */, 6CA208292A191185001C4247 /* UIImageView+.swift in Sources */, 0982DE5A29AE5E6000D933D2 /* CompositionalLayout.swift in Sources */, + 09DC0EF12BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift in Sources */, 098904522B81CA33004AAD3C /* UpdateCoordinatorImpl.swift in Sources */, 098904442B81C18A004AAD3C /* HomeCoordinator.swift in Sources */, 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */, @@ -1478,6 +1524,7 @@ 098BFD5929B7999E008E80F9 /* MyProfileCollectionViewCell.swift in Sources */, 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */, 3B5F8F7A29BF8E8D0063A7F8 /* AddMissionProtocol.swift in Sources */, + 09DC0F012BAEC27A0075AAC9 /* AuthViewControllerFactory.swift in Sources */, 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */, 09F6718C29CB4AB700708725 /* SubOnboardingCollectionViewCell.swift in Sources */, 0921611D2A57D0920019CC8C /* AmplitudeAnalyticsService.swift in Sources */, @@ -1486,6 +1533,7 @@ 098BFD5B29B79B6A008E80F9 /* MyInfoModel.swift in Sources */, 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */, 6CF4707A29A7AAFF008D145C /* PaddingLabel.swift in Sources */, + 09DC0EFD2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift in Sources */, 6CA208272A18FFCF001C4247 /* RecommendResponseDTO.swift in Sources */, 3B4E12F22A27B621001D1EC1 /* NottodoModalViewController.swift in Sources */, 3B027A78299C31B500BEB65C /* AppDelegate.swift in Sources */, @@ -1529,6 +1577,7 @@ 098904302B81BB3A004AAD3C /* Coordinator.swift in Sources */, 3B14A13D29A6FBD300F92897 /* UIView+.swift in Sources */, 09F6719529CBFCD200708725 /* GradientView.swift in Sources */, + 09DC0F052BAEC2850075AAC9 /* AchieveViewControllerFactory.swift in Sources */, 09DCB8622BA031F600B6BB74 /* AchieveManagerImpl.swift in Sources */, 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */, 6CA208252A18FEEA001C4247 /* RecommendAPI.swift in Sources */, @@ -1539,11 +1588,13 @@ 09582B4829BDA7F600EF3207 /* DetailStackView.swift in Sources */, 09F6718429CADB1100708725 /* OnboardingModel.swift in Sources */, 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */, + 09DC0EF32BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift in Sources */, 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */, 0989043E2B81BD50004AAD3C /* CoordinatorFactory.swift in Sources */, 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */, 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */, 09DCB8582BA0309F00B6BB74 /* ErrorReponse.swift in Sources */, + 09DC0F032BAEC2800075AAC9 /* MyPageViewControllerFactory.swift in Sources */, 0982DE5829AE40FB00D933D2 /* UITabBar+.swift in Sources */, 3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */, 6C16015829C40112005AE3F5 /* AuthButtonView.swift in Sources */, @@ -1571,6 +1622,8 @@ 0987C8402B9DD4DC007EE8DE /* MissionAPI.swift in Sources */, 6CD4F8C229AA5AF200CCC740 /* UIButton+.swift in Sources */, 6CA208362A1957CA001C4247 /* AuthService.swift in Sources */, + 09DC0EFF2BAEC2750075AAC9 /* HomeViewControllerFactory.swift in Sources */, + 09DC0EF52BAEC2030075AAC9 /* HomeFlowControllerFactory.swift in Sources */, 6CA208362A1957CA001C4247 /* AuthService.swift in Sources */, 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */, 6C16016429C5E37D005AE3F5 /* MyInfoAccountStackView.swift in Sources */, @@ -1591,6 +1644,7 @@ 09ED941B2B2ABAB7001864EF /* CommonNotificationViewController.swift in Sources */, 6C9628A92A22209E003ADE25 /* LogoOnboardingViewController.swift in Sources */, 09F6718029CAD76C00708725 /* SecondOnboardingViewController.swift in Sources */, + 09DC0EFB2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift in Sources */, 098A23A42B833F0300265955 /* AuthCoordinatorImpl.swift in Sources */, 098BFD5D29B79CE3008E80F9 /* InfoCollectionViewCell.swift in Sources */, 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */, @@ -1599,6 +1653,7 @@ 09DCB8562BA0308D00B6BB74 /* APIError.swift in Sources */, 098904402B81BFAF004AAD3C /* ViewControllerFactory.swift in Sources */, 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */, + 09DC0EF72BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift in Sources */, 3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */, 3B4E12F62A27C0BE001D1EC1 /* QuitModalView.swift in Sources */, 6CA208232A18FE78001C4247 /* RecommendService.swift in Sources */, @@ -1607,6 +1662,7 @@ 09DCB8692BA05F9E00B6BB74 /* AchievementModel.swift in Sources */, 098904542B81CA47004AAD3C /* UpdateCoordinator.swift in Sources */, 0989044E2B81C216004AAD3C /* MypageCoordinatorImpl.swift in Sources */, + 09DC0F072BAEC2890075AAC9 /* TabBarViewControllerFactory.swift in Sources */, 09DCB8652BA056C800B6BB74 /* DetailAchievementModel.swift in Sources */, 09DCB8602BA031E000B6BB74 /* AchieveManager.swift in Sources */, ); diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift new file mode 100644 index 00000000..602f2f31 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift @@ -0,0 +1,38 @@ +// +// AchieveViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + + func makeAchieveViewModel(coordinator: AchieveCoordinator) -> any AchievementViewModel { + let missionAPI = DefaultMissionService() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = AchievementViewModelImpl(coordinator: coordinator, manager: manager) + return viewModel + } + + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { + let viewModel = self.makeAchieveViewModel(coordinator: coordinator) + let viewController = AchievementViewController(viewModel: viewModel) + return viewController + } + + func makeAchieveDetailViewModel(coordinator: AchieveCoordinator) -> any DetailAchievementViewModel & DetailAchievementViewModelPresentable { + let missionAPI = DefaultMissionService() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, manager: manager) + return viewModel + } + + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController { + let viewModel = self.makeAchieveDetailViewModel(coordinator: coordinator) + viewModel.selectedDate(date) + let viewController = DetailAchievementViewController(viewModel: viewModel) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift new file mode 100644 index 00000000..87ac3689 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift @@ -0,0 +1,52 @@ +// +// AuthViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController { + let viewController = AuthViewController(coordinator: coordinator) + return viewController + } + + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController { + let viewController = NotificationDialogViewController() + viewController.buttonHandler = { + completion() + } + return viewController + } +} + +// Onboarding +extension ViewControllerFactoryImpl { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController { + let viewController = ValueOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController { + let viewController = LogoOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController { + let viewController = SecondOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController { + let viewController = ThirdOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController { + let viewController = FourthOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController { + let viewController = FifthOnboardingViewController(coordinator: coordinator) + return viewController + } +} + diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift new file mode 100644 index 00000000..f5730ef2 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift @@ -0,0 +1,91 @@ +// +// HomeViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +// Home +extension ViewControllerFactoryImpl { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController { + let viewController = CommonNotificationViewController(coordinator: coordinator) + viewController.tapCloseButton = { + completion() + } + return viewController + } + + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController { + let viewController = HomeViewController(coordinator: coordinator) + return viewController + } +} + +// HomeDetail +extension ViewControllerFactoryImpl { + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController { + let viewController = HomeDeleteViewController(coordinator: coordinator) + viewController.deleteClosure = { + completion() + } + return viewController + } + + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController { + let viewController = MissionDetailViewController(coordinator: coordinator) + viewController.userId = id + viewController.deleteClosure = { + deleteClosure() + } + viewController.moveDateClosure = { date in + moveDateClosure(date) + } + return viewController + } + + func makeModifyViewController(coordinator: HomeCoordinator, id: Int = 0, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setMissionId(id) + return viewController + } + + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController { + let viewController = DetailCalendarViewController(coordinator: coordinator) + viewController.detailModel = data + viewController.userId = id + viewController.movedateClosure = { date in + moveDateClosure(date) + } + return viewController + } +} + +// Add Mission +extension ViewControllerFactoryImpl { + + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController { + let viewController = RecommendViewController(coordinator: coordinator) + viewController.setSelectDate(date) + return viewController + } + + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController { + let viewController = RecommendActionViewController(coordinator: coordinator) + viewController.actionHeaderData = data + return viewController + } + + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setNottodoLabel(data.nottodo ?? "") + viewController.setSituationLabel(data.situation ?? "") + viewController.setActionLabel(data.action ?? "") + viewController.setDate(data.date) + + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift new file mode 100644 index 00000000..64d8812f --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift @@ -0,0 +1,25 @@ +// +// MyPageViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController { + let viewController = MyInfoViewController(coordinator: coordinator) + return viewController + } + + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController { + let viewController = MyInfoAccountViewController(coordinator: coordinator) + return viewController + } + + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController { + let viewController = NottodoModalViewController(coordinator: coordinator) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift new file mode 100644 index 00000000..ac76903e --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift @@ -0,0 +1,13 @@ +// +// AchieveFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol AchieveFlowControllerFactory { + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift new file mode 100644 index 00000000..45e5cff5 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift @@ -0,0 +1,18 @@ +// +// AuthFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation +protocol AuthFlowControllerFactory { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift new file mode 100644 index 00000000..c57717a8 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift @@ -0,0 +1,20 @@ +// +// HomeFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol HomeFlowControllerFactory { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController + func makeModifyViewController(coordinator: HomeCoordinator, id: Int, type: MissionType) -> AddMissionViewController + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift new file mode 100644 index 00000000..0ef669c8 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift @@ -0,0 +1,14 @@ +// +// MyPageFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol MyPageFlowControllerFactory { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift new file mode 100644 index 00000000..b37b98ca --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift @@ -0,0 +1,12 @@ +// +// TabBarFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import UIKit + +protocol TabBarControllerFactory { + func makeTabBarController(_ navigationController: UINavigationController) -> (UITabBarController, [UINavigationController]) +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift new file mode 100644 index 00000000..81aa1343 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift @@ -0,0 +1,12 @@ +// +// UpdateFlowcontrollerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol UpdateFlowcontrollerFactory { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift new file mode 100644 index 00000000..c608f4af --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift @@ -0,0 +1,24 @@ +// +// TabBarViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import UIKit + +extension ViewControllerFactoryImpl { + func makeTabBarController(_: UINavigationController) -> (UITabBarController, [UINavigationController]) { + let tabBarController = TabBarController() + let navigationControllers = tabBarController.setTabBarItems().map(makeNavigationController) + + return (tabBarController, navigationControllers) + } + + func makeNavigationController(_ tabBarItem: UITabBarItem) -> UINavigationController { + let navigationController = UINavigationController() + navigationController.tabBarItem = tabBarItem + navigationController.setNavigationBarHidden(true, animated: false) + return navigationController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift new file mode 100644 index 00000000..db6d76ed --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift @@ -0,0 +1,15 @@ +// +// UpdateViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController { + let viewController = UpdateCheckViewController(coordinator: coordinator) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift index e1ebf681..b8381e89 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift @@ -9,230 +9,4 @@ import UIKit protocol ViewControllerFactory: UpdateFlowcontrollerFactory, AuthFlowControllerFactory, HomeFlowControllerFactory, MyPageFlowControllerFactory, AchieveFlowControllerFactory, TabBarControllerFactory, AuthFlowControllerFactory {} -protocol UpdateFlowcontrollerFactory { - func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController -} - -protocol AuthFlowControllerFactory { - func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController - func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController - func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController - func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController - func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController - func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController - func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController - func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController - -} - -protocol HomeFlowControllerFactory { - func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController - func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController - func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController - func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController - func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController - func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController - func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController - func makeModifyViewController(coordinator: HomeCoordinator, id: Int, type: MissionType) -> AddMissionViewController - func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController -} - -protocol MyPageFlowControllerFactory { - func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController - func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController - func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController -} - -protocol AchieveFlowControllerFactory { - func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController -} - -protocol TabBarControllerFactory { - func makeTabBarController(_ navigationController: UINavigationController) -> (UITabBarController, [UINavigationController]) -} - final class ViewControllerFactoryImpl: ViewControllerFactory {} - -// update -extension ViewControllerFactoryImpl { - func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController { - let viewController = UpdateCheckViewController(coordinator: coordinator) - return viewController - } -} -// onboarding -extension ViewControllerFactoryImpl { - func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController { - let viewController = ValueOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController { - let viewController = LogoOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController { - let viewController = SecondOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController { - let viewController = ThirdOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController { - let viewController = FourthOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController { - let viewController = FifthOnboardingViewController(coordinator: coordinator) - return viewController - } -} -// auth -extension ViewControllerFactoryImpl { - func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController { - let viewController = AuthViewController(coordinator: coordinator) - return viewController - } - - func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController { - let viewController = NotificationDialogViewController() - viewController.buttonHandler = { - completion() - } - return viewController - } -} -// home -extension ViewControllerFactoryImpl { - func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController { - let viewController = CommonNotificationViewController(coordinator: coordinator) - viewController.tapCloseButton = { - completion() - } - return viewController - } - - func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController { - let viewController = HomeViewController(coordinator: coordinator) - return viewController - } -} - -// add mission -extension ViewControllerFactoryImpl { - - func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController { - let viewController = RecommendViewController(coordinator: coordinator) - viewController.setSelectDate(date) - return viewController - } - - func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController { - let viewController = RecommendActionViewController(coordinator: coordinator) - viewController.actionHeaderData = data - return viewController - } - - func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController { - let viewController = AddMissionViewController(coordinator: coordinator) - viewController.setViewType(type) - viewController.setNottodoLabel(data.nottodo ?? "") - viewController.setSituationLabel(data.situation ?? "") - viewController.setActionLabel(data.action ?? "") - viewController.setDate(data.date) - - return viewController - } -} -// home detail -extension ViewControllerFactoryImpl { - func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController { - let viewController = HomeDeleteViewController(coordinator: coordinator) - viewController.deleteClosure = { - completion() - } - return viewController - } - - func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController { - let viewController = MissionDetailViewController(coordinator: coordinator) - viewController.userId = id - viewController.deleteClosure = { - deleteClosure() - } - viewController.moveDateClosure = { date in - moveDateClosure(date) - } - return viewController - } - - func makeModifyViewController(coordinator: HomeCoordinator, id: Int = 0, type: MissionType) -> AddMissionViewController { - let viewController = AddMissionViewController(coordinator: coordinator) - viewController.setViewType(type) - viewController.setMissionId(id) - return viewController - } - - func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController { - let viewController = DetailCalendarViewController(coordinator: coordinator) - viewController.detailModel = data - viewController.userId = id - viewController.movedateClosure = { date in - moveDateClosure(date) - } - return viewController - } -} -// mypage -extension ViewControllerFactoryImpl { - func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController { - let viewController = MyInfoViewController(coordinator: coordinator) - return viewController - } - - func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController { - let viewController = MyInfoAccountViewController(coordinator: coordinator) - return viewController - } - - func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController { - let viewController = NottodoModalViewController(coordinator: coordinator) - return viewController - } -} -// achieve -extension ViewControllerFactoryImpl { - func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { - let missionAPI = DefaultMissionService() - let manager = AchieveManagerImpl(missionAPI: missionAPI) - let viewModel = AchievementViewModelImpl(coordinator: coordinator, manager: manager) - let viewController = AchievementViewController(viewModel: viewModel) - return viewController - } - - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController { - let missionAPI = DefaultMissionService() - let manager = AchieveManagerImpl(missionAPI: missionAPI) - let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, manager: manager) - viewModel.selectedDate(date) - let viewController = DetailAchievementViewController(viewModel: viewModel) - return viewController - } -} -// tabbar -extension ViewControllerFactoryImpl { - func makeTabBarController(_: UINavigationController) -> (UITabBarController, [UINavigationController]) { - let tabBarController = TabBarController() - let navigationControllers = tabBarController.setTabBarItems().map(makeNavigationController) - - return (tabBarController, navigationControllers) - } - - func makeNavigationController(_ tabBarItem: UITabBarItem) -> UINavigationController { - let navigationController = UINavigationController() - navigationController.tabBarItem = tabBarItem - navigationController.setNavigationBarHidden(true, animated: false) - return navigationController - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 0997a85b..648d1ecf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -12,7 +12,7 @@ import FSCalendar import Then import SnapKit -final class AchievementViewController: UIViewController, AchievementViewModelPresentable { +final class AchievementViewController: UIViewController { // MARK: - Properties From 69e59bb3e9cb6578b4f4cfb6c45ccbb06911bcf3 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sat, 23 Mar 2024 18:17:23 +0900 Subject: [PATCH 21/21] =?UTF-8?q?[Fix]=20#241=20-=20=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Coordinator/Factory/AuthViewControllerFactory.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift index 87ac3689..f30455f3 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift @@ -49,4 +49,3 @@ extension ViewControllerFactoryImpl { return viewController } } -