From eb42f1017c1c9754e06cd9f2950c66202e18eced Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:28:36 +0900 Subject: [PATCH 01/26] =?UTF-8?q?[Refactor]=20#195=20-=20=EB=82=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20UI=20=EB=AA=A8=EB=8D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfo/Model/MyInfoModel.swift | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift index 396aabba..0a1845f4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift @@ -7,36 +7,23 @@ import UIKit -struct InfoModelOne: Hashable { - var image: UIImage - var user: String - var email: String -} - -struct InfoModelTwo: Hashable { - var image: UIImage - var title: String -} -extension InfoModelTwo { - static let items: [InfoModelTwo] = [InfoModelTwo(image: .icGuide, title: "낫투두 가이드"), - InfoModelTwo(image: .icQuestion1, title: "자주 묻는 질문") - ] -} - -struct InfoModelThree: Hashable { - var title: String -} -extension InfoModelThree { - static let items: [InfoModelThree] = [InfoModelThree(title: "공지사항"), - InfoModelThree(title: "문의하기"), - InfoModelThree(title: "약관 및 정책") - ] -} - -struct InfoModelFour: Hashable { - var title: String -} -extension InfoModelFour { - - static let item: [InfoModelFour] = [InfoModelFour(title: "버전 정보 "+(Utils.version ?? "1.0.0"))] +struct InfoModel: Hashable { + + var image: UIImage? + var user: String? + var email: String? + var title: String? + + static var profile: [InfoModel] = [InfoModel(image: .imgUser, + user: UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) ? KeychainUtil.getAppleUsername() : KeychainUtil.getKakaoNickname(), + email: UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) ? KeychainUtil.getAppleEmail() : KeychainUtil.getKakaoEmail())] + + static let support: [InfoModel] = [InfoModel(image: .icGuide, title: "낫투두 가이드"), + InfoModel(image: .icQuestion1, title: "자주 묻는 질문") + ] + static let info: [InfoModel] = [InfoModel(title: "공지사항"), + InfoModel(title: "문의하기"), + InfoModel(title: "약관 및 정책") + ] + static func version() -> [InfoModel] { return [InfoModel(title: "버전 정보 "+(Utils.version ?? "1.0.0"))] } } From e570acb42526fb7e7fae6b889ef46914a275bb43 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:28:52 +0900 Subject: [PATCH 02/26] =?UTF-8?q?[Fix]=20#195=20-=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20info=20cell=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfo/Cell/InfoCollectionViewCell.swift | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift index 84e7c230..5aaf5c92 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift @@ -7,7 +7,10 @@ import UIKit -class InfoCollectionViewCell: UICollectionViewCell { +import SnapKit +import Then + +final class InfoCollectionViewCell: UICollectionViewCell { // MARK: - Properties @@ -20,6 +23,9 @@ class InfoCollectionViewCell: UICollectionViewCell { private let titleLabel = UILabel() private let arrowImage = UIImageView() + + // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: .zero) setUI() @@ -34,6 +40,7 @@ class InfoCollectionViewCell: UICollectionViewCell { // MARK: - Methods extension InfoCollectionViewCell { + private func setUI() { backgroundColor = .gray1 @@ -47,6 +54,7 @@ extension InfoCollectionViewCell { $0.axis = .horizontal $0.spacing = 6 } + arrowImage.do { $0.isHidden = true $0.image = .icRightArrow @@ -73,7 +81,8 @@ extension InfoCollectionViewCell { } } - func configureWithIcon(model: InfoModelTwo) { + func configureWithIcon(with model: InfoModel) { + iconImage.image = model.image titleLabel.text = model.title @@ -82,17 +91,10 @@ extension InfoCollectionViewCell { } } - func configureWithArrow(model: InfoModelThree) { - horizontalStackView.removeArrangedSubview(iconImage) - iconImage.removeFromSuperview() - titleLabel.text = model.title - arrowImage.isHidden = false - } - - func configure(model: InfoModelFour) { + func configure(with model: InfoModel, isHidden: Bool) { horizontalStackView.removeArrangedSubview(iconImage) iconImage.removeFromSuperview() titleLabel.text = model.title - arrowImage.isHidden = true + arrowImage.isHidden = isHidden } } From 35a4c41df1b31164ef2f0379d36d59ce45178910 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:30:13 +0900 Subject: [PATCH 03/26] =?UTF-8?q?[Fix]=20#195=20-=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20profile=20cell=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfo/Cell/MyProfileCollectionViewCell.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift index 95ded053..e0661fce 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift @@ -7,7 +7,10 @@ import UIKit -class MyProfileCollectionViewCell: UICollectionViewCell { +import SnapKit +import Then + +final class MyProfileCollectionViewCell: UICollectionViewCell { // MARK: - Properties @@ -19,6 +22,8 @@ class MyProfileCollectionViewCell: UICollectionViewCell { private let verticalStackView = UIStackView() private let userLabel = UILabel() private let emailLabel = UILabel() + + // MARK: - Life Cycle override init(frame: CGRect) { super.init(frame: .zero) @@ -34,6 +39,7 @@ class MyProfileCollectionViewCell: UICollectionViewCell { // MARK: - Methods extension MyProfileCollectionViewCell { + private func setUI() { backgroundColor = .gray1 @@ -43,12 +49,14 @@ extension MyProfileCollectionViewCell { $0.textColor = .white $0.font = .Pretendard(.regular, size: 15) } + emailLabel.do { $0.textAlignment = .left $0.numberOfLines = 1 $0.textColor = .gray4 $0.font = .Pretendard(.regular, size: 12) } + verticalStackView.do { $0.addArrangedSubviews(userLabel, emailLabel) $0.axis = .vertical @@ -73,7 +81,7 @@ extension MyProfileCollectionViewCell { } } - func configure(model: InfoModelOne) { + func configure(model: InfoModel) { logoImage.image = model.image userLabel.text = model.user emailLabel.text = model.email From 463f8bf1018be414ebb149b347accf0710f1438a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:38:51 +0900 Subject: [PATCH 04/26] =?UTF-8?q?[Chore]=20#195=20-=20=EB=82=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20header=20UI=20=EB=84=A4=EC=9D=B4=EB=B0=8D?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Cell/MyInfoHeaderCollectionReusableView.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift index e29784e4..aecc8d6d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift @@ -10,18 +10,21 @@ import UIKit import Then import SnapKit -class MyInfoHeaderReusableView: UICollectionReusableView { +final class MyInfoHeaderView: UICollectionReusableView { // MARK: - Properties - static let identifier = "MyInfoHeaderReusableView" + static let identifier = "MyInfoHeaderView" // MARK: - UI Components private let myInfoLabel = UILabel() + // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: .zero) + setUI() setLayout() } @@ -33,7 +36,8 @@ class MyInfoHeaderReusableView: UICollectionReusableView { // MARK: - Methods -extension MyInfoHeaderReusableView { +extension MyInfoHeaderView { + private func setUI() { myInfoLabel.do { $0.text = I18N.myInfo @@ -41,6 +45,7 @@ extension MyInfoHeaderReusableView { $0.textColor = .white } } + private func setLayout() { addSubview(myInfoLabel) From bca1f67abe07a69bb9027aa78bdb2a1135c81f3e Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:39:14 +0900 Subject: [PATCH 05/26] =?UTF-8?q?[Fix]=20#195=20-=20lint=20=EC=98=A4?= =?UTF-8?q?=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 --- .../Presentation/MyInfo/Cell/InfoCollectionViewCell.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift index 5aaf5c92..96120628 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift @@ -23,7 +23,6 @@ final class InfoCollectionViewCell: UICollectionViewCell { private let titleLabel = UILabel() private let arrowImage = UIImageView() - // MARK: - Life Cycle override init(frame: CGRect) { From 787f9ca40a4df6b44de99fd2e9053aab30e20a3a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:39:39 +0900 Subject: [PATCH 06/26] =?UTF-8?q?[Fix]=20#195=20-=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20URL=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Global/Enum/MyInfoURL.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/MyInfoURL.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/MyInfoURL.swift index a530036c..b8b554f4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/MyInfoURL.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/MyInfoURL.swift @@ -10,16 +10,16 @@ import Foundation import SafariServices enum MyInfoURL { - case guid, quesition, notice, contact, service, personalInfo, googleForm, opensource + case guid, faq, notice, question, service, personalInfo, googleForm var url: String { switch self { case .guid: return "https://teamnottodo.notion.site/f35a7f2d6d5c4b33b4d0949f6077e6cd" - case .quesition: + case .faq: return "https://teamnottodo.notion.site/a6ef7036bde24e289e576ace099f39dc" case .notice: return "https://teamnottodo.notion.site/a5dbb310ec1d43baae02b7e9bf0b3411" - case .contact: + case .question: return "http://pf.kakao.com/_fUIQxj/chat" case .service: return "https://teamnottodo.notion.site/81594da775614d23900cdb2475eadb73?pvs=4" @@ -27,8 +27,6 @@ enum MyInfoURL { return "https://teamnottodo.notion.site/5af34df7da3649fc941312c5f533c1eb" case .googleForm: return "https://forms.gle/gwBJ4hL4bCTjXRTP6" - case .opensource: - return "https://teamnottodo.notion.site/a391274a627643f6a4d1f2412d4cf170?pvs=4" } } From 7427d02e8f994d9f972c715dc35046f289d01f93 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:51:21 +0900 Subject: [PATCH 07/26] =?UTF-8?q?[Fix]=20#195=20-=20Custom=20Layout=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 --- .../Common/Layout/CompositionalLayout.swift | 40 ++++++++++++------- .../ViewControllers/HomeViewController.swift | 2 +- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift index bf78e680..5ff7e31a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Layout/CompositionalLayout.swift @@ -7,31 +7,41 @@ import UIKit -final class CompositionalLayout { +struct CompositionalLayout { - class func _vertical(_ itemWidth: NSCollectionLayoutDimension, _ itemHeight: NSCollectionLayoutDimension, _ groupWidth: NSCollectionLayoutDimension, _ groupHeight: NSCollectionLayoutDimension, count: Int, edge: NSDirectionalEdgeInsets?) -> NSCollectionLayoutSection { + static func vertical(itemWidth: NSCollectionLayoutDimension = .fractionalWidth(1), + itemHeight: NSCollectionLayoutDimension = .fractionalHeight(1), + groupWidth: NSCollectionLayoutDimension = .fractionalWidth(1), + groupHeight: NSCollectionLayoutDimension = .fractionalHeight(1), + count: Int, + edge: NSDirectionalEdgeInsets = .zero) -> NSCollectionLayoutSection { let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: itemWidth, heightDimension: itemHeight)) - let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: groupWidth, heightDimension: groupHeight), subitem: item, count: count ) - return section(group, edge ?? NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: groupWidth, heightDimension: groupHeight), subitem: item, count: count) + return createSection(group, edge) } - class func section(_ group: NSCollectionLayoutGroup, _ edge: NSDirectionalEdgeInsets) -> NSCollectionLayoutSection { + static func createSection(_ group: NSCollectionLayoutGroup, _ edge: NSDirectionalEdgeInsets) -> NSCollectionLayoutSection { let section = NSCollectionLayoutSection(group: group) section.contentInsets = edge return section } - class func setUpSection(layoutEnvironment: NSCollectionLayoutEnvironment, mode: UICollectionLayoutListConfiguration.HeaderMode, _ top: CGFloat, _ bottom: CGFloat) -> NSCollectionLayoutSection { - var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) - config.headerMode = mode - config.showsSeparators = true - config.separatorConfiguration.color = UIColor.gray2! - config.backgroundColor = .clear - config.headerTopPadding = 22 + static func setUpSection(layoutEnvironment: NSCollectionLayoutEnvironment, + mode: UICollectionLayoutListConfiguration.HeaderMode = .none, + topContentInset: CGFloat = 0, + bottomContentInset: CGFloat = 0) + -> NSCollectionLayoutSection { + var listConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped) + listConfig.headerMode = mode + listConfig.showsSeparators = true + listConfig.separatorConfiguration.color = UIColor.gray2! + listConfig.backgroundColor = .clear + listConfig.headerTopPadding = 22 - let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) - section.contentInsets = NSDirectionalEdgeInsets(top: top, leading: 0, bottom: bottom, trailing: 0) - if config.headerMode == .supplementary { + let section = NSCollectionLayoutSection.list(using: listConfig, layoutEnvironment: layoutEnvironment) + section.contentInsets = NSDirectionalEdgeInsets(top: topContentInset, leading: 0, bottom: bottomContentInset, trailing: 0) + + if listConfig.headerMode == .supplementary { let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(22)) let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top) section.boundarySupplementaryItems = [header] diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 8d75533a..0f1b194c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -205,7 +205,7 @@ extension HomeViewController { return layoutSection case .empty: - return CompositionalLayout._vertical(.fractionalWidth(1), .fractionalWidth(1), .fractionalWidth(1), .fractionalWidth(1), count: 1, edge: .init(top: 30, leading: 0, bottom: 0, trailing: 0)) + return CompositionalLayout.vertical(itemWidth: .fractionalWidth(1), itemHeight: .fractionalWidth(1), groupWidth: .fractionalWidth(1), groupHeight: .fractionalWidth(1), count: 1, edge: .init(top: 30, leading: 0, bottom: 0, trailing: 0)) } } return layout From bdb7aab42bfc23acd6446e43a2961973f2583dac Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 15:51:49 +0900 Subject: [PATCH 08/26] =?UTF-8?q?[Refactor]=20#195=20-=20=EB=82=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20VC=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfoViewController.swift | 197 ++++++++++-------- 1 file changed, 115 insertions(+), 82 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift index 6435c13b..311e7226 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift @@ -14,48 +14,45 @@ final class MyInfoViewController: UIViewController { // MARK: - Properties - private var infoOne: [InfoModelOne] = [InfoModelOne(image: .imgUser, user: UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) ? KeychainUtil.getAppleUsername() : KeychainUtil.getKakaoNickname(), email: UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) ? KeychainUtil.getAppleEmail() : KeychainUtil.getKakaoEmail())] - private let infoTwo: [InfoModelTwo] = InfoModelTwo.items - private let infoThree: [InfoModelThree] = InfoModelThree.items - private let infoFour: [InfoModelFour] = InfoModelFour.item + typealias CellRegistration = UICollectionView.CellRegistration + typealias HeaderRegistration = UICollectionView.SupplementaryRegistration + typealias DataSource = UICollectionViewDiffableDataSource + typealias SnapShot = NSDiffableDataSourceSnapshot - enum Sections: Int, Hashable { - case one, two, three, four + enum Sections: Int, CaseIterable { + case profile, support, info, version } - typealias DataSource = UICollectionViewDiffableDataSource - var dataSource: DataSource? + + private var dataSource: DataSource? + private lazy var safeArea = self.view.safeAreaLayoutGuide // MARK: - UI Components - private lazy var myInfoCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + private let myInfoCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) // MARK: - Life Cycle - + override func viewDidLoad() { super.viewDidLoad() + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.viewMyInfo) setUI() - register() setLayout() setupDataSource() - reloadData() + setSanpShot() } } // MARK: - Methods extension MyInfoViewController { - private func register() { - myInfoCollectionView.register(MyProfileCollectionViewCell.self, forCellWithReuseIdentifier: MyProfileCollectionViewCell.identifier) - myInfoCollectionView.register(InfoCollectionViewCell.self, forCellWithReuseIdentifier: InfoCollectionViewCell.identifier) - myInfoCollectionView.register(MyInfoHeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: MyInfoHeaderReusableView.identifier) - } private func setUI() { view.backgroundColor = .ntdBlack myInfoCollectionView.do { + $0.collectionViewLayout = layout() $0.backgroundColor = .clear $0.bounces = false $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -73,100 +70,136 @@ extension MyInfoViewController { } } - // MARK: - Data - private func setupDataSource() { + + let profileCellRegistration = CellRegistration {cell, _, item in + cell.configure(model: item) + } + + let infoCellRegistration = CellRegistration {cell, indexPath, item in + + guard let section = Sections(rawValue: indexPath.section) else { return } + + switch section { + case .support: + cell.configureWithIcon(with: item) + case .info: + cell.configure(with: item, isHidden: false) + default: + cell.configure(with: item, isHidden: true) + } + } + + let headerRegistration = HeaderRegistration(elementKind: UICollectionView.elementKindSectionHeader) { _, _, _ in } + dataSource = DataSource(collectionView: myInfoCollectionView, cellProvider: { collectionView, indexPath, item in - let section = self.dataSource?.snapshot().sectionIdentifiers[indexPath.section] + + guard let section = Sections(rawValue: indexPath.section) else { return UICollectionViewCell() } + switch section { - case .one: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyProfileCollectionViewCell.identifier, for: indexPath) as! MyProfileCollectionViewCell - cell.configure(model: item as! InfoModelOne ) - return cell - case .two: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InfoCollectionViewCell.identifier, for: indexPath) as! InfoCollectionViewCell - cell.configureWithIcon(model: item as! InfoModelTwo ) - return cell - case .three: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InfoCollectionViewCell.identifier, for: indexPath) as! InfoCollectionViewCell - cell.configureWithArrow(model: item as! InfoModelThree) - return cell - case .four, .none: - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InfoCollectionViewCell.identifier, for: indexPath) as! InfoCollectionViewCell - cell.configure(model: item as! InfoModelFour) - return cell + case .profile: + return collectionView.dequeueConfiguredReusableCell(using: profileCellRegistration, + for: indexPath, + item: item) + case .support, .info, .version: + return collectionView.dequeueConfiguredReusableCell(using: infoCellRegistration, + for: indexPath, + item: item) } }) + + dataSource?.supplementaryViewProvider = { collectionView, _, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, + for: indexPath) + } } - private func reloadData() { - var snapShot = NSDiffableDataSourceSnapshot() + private func setSanpShot() { + + var snapShot = SnapShot() + defer { dataSource?.apply(snapShot, animatingDifferences: false) } - snapShot.appendSections([.one, .two, .three, .four]) - snapShot.appendItems(infoOne, toSection: .one) - snapShot.appendItems(infoTwo, toSection: .two) - snapShot.appendItems(infoThree, toSection: .three) - snapShot.appendItems(infoFour, toSection: .four) - dataSource?.supplementaryViewProvider = { (collectionView, _, indexPath) in - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: MyInfoHeaderReusableView.identifier, for: indexPath) as? MyInfoHeaderReusableView else { return UICollectionReusableView() } - return header - } + snapShot.appendSections(Sections.allCases) + snapShot.appendItems(InfoModel.profile, toSection: .profile) + snapShot.appendItems(InfoModel.support, toSection: .support) + snapShot.appendItems(InfoModel.info, toSection: .info) + snapShot.appendItems(InfoModel.version(), toSection: .version) + } - // MARK: - Layout - private func layout() -> UICollectionViewLayout { + let layout = UICollectionViewCompositionalLayout { sectionIndex, env in - let section = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] + + let section = Sections(rawValue: sectionIndex) switch section { - case .one: - return CompositionalLayout.setUpSection(layoutEnvironment: env, mode: .supplementary, 24, 0) - case .two, .three: - return CompositionalLayout.setUpSection(layoutEnvironment: env, mode: .none, 18, 0) - case .four, .none: - return CompositionalLayout.setUpSection(layoutEnvironment: env, mode: .none, 18, 60) + case .profile: + return CompositionalLayout.setUpSection(layoutEnvironment: env, + mode: .supplementary, + topContentInset: 24) + case .support, .info: + return CompositionalLayout.setUpSection(layoutEnvironment: env, + topContentInset: 18) + case .version, .none: + return CompositionalLayout.setUpSection(layoutEnvironment: env, + topContentInset: 18, + bottomContentInset: 60) + } + } return layout } } + +// MARK: - CollectionViewDelegate + extension MyInfoViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { switch indexPath.section { case 0: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickMyInfo) - let nextViewController = MyInfoAccountViewController() - nextViewController.hidesBottomBarWhenPushed = false - navigationController?.pushViewController(nextViewController, animated: true) + ProfileSectionSelection() case 1: - switch indexPath.item { - case 0: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickGuide) - Utils.myInfoUrl(vc: self, url: MyInfoURL.guid.url) - default: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickFaq) - Utils.myInfoUrl(vc: self, url: MyInfoURL.quesition.url) - } + InfoSectionSelection(for: indexPath, + events: [.clickGuide, .clickFaq], + urls: [.guid, .faq]) case 2: - switch indexPath.item { - case 0: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickNotice) - Utils.myInfoUrl(vc: self, url: MyInfoURL.notice.url) - case 1: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickQuestion) - Utils.myInfoUrl(vc: self, url: MyInfoURL.contact.url) - case 2: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickTerms) - Utils.myInfoUrl(vc: self, url: MyInfoURL.service.url) - default: - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.MyInfo.clickOpenSource) - Utils.myInfoUrl(vc: self, url: MyInfoURL.opensource.url) - } + InfoSectionSelection(for: indexPath, + events: [.clickNotice, .clickQuestion, .clickTerms], + urls: [.notice, .question, .service]) default: return } } + + private func ProfileSectionSelection() { + sendAnalyticsEvent(.clickMyInfo) { + + let nextViewController = MyInfoAccountViewController() + nextViewController.hidesBottomBarWhenPushed = false + navigationController?.pushViewController(nextViewController, animated: true) + } + } + + private func InfoSectionSelection(for indexPath: IndexPath, + events: [AnalyticsEvent.MyInfo], + urls: [MyInfoURL]) { + guard let item = urls.indices.contains(indexPath.item) ? urls[indexPath.item] : nil, + let event = events.indices.contains(indexPath.item) ? events[indexPath.item] : nil else { + return + } + + sendAnalyticsEvent(event) { + Utils.myInfoUrl(vc: self, url: item.url) + } + } + + private func sendAnalyticsEvent(_ event: AnalyticsEvent.MyInfo, action: () -> Void) { + AmplitudeAnalyticsService.shared.send(event: event) + action() + } } From b6719f9304f2aca506ab9bf38a12572503176dc7 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:04:31 +0900 Subject: [PATCH 09/26] =?UTF-8?q?[Fix]=20#196=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Achievement/ViewControllers/StatisticsView.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift index 3f989243..4b56838f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift @@ -10,7 +10,7 @@ import UIKit import Then import SnapKit -class StatisticsView: UIView { +final class StatisticsView: UIView { // MARK: - UI Components @@ -21,6 +21,7 @@ class StatisticsView: UIView { override init(frame: CGRect) { super.init(frame: .zero) + setUI() setLayout() } @@ -33,10 +34,13 @@ class StatisticsView: UIView { // MARK: - Methods extension StatisticsView { + private func setUI() { + totalImage.do { $0.image = .icSNS } + titleLabel.do { $0.text = I18N.total $0.font = .Pretendard(.regular, size: 14) @@ -45,6 +49,7 @@ extension StatisticsView { $0.textAlignment = .center } } + private func setLayout() { addSubviews(totalImage, titleLabel) From a5c352f6c958cebc95f6ab4bd0d30feefbe9b1f3 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:04:59 +0900 Subject: [PATCH 10/26] =?UTF-8?q?[Feat]=20#196=20-=20=EB=94=94=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=84=B1=EC=B7=A8=20header=20UI=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 --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 8 ++- .../DetailAchieveHeaderView.swift | 64 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..dfacb087 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */; }; 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */; }; 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; + 09CF56022B09E98A00526C8C /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */; }; 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; }; 09DCCD212A18EF43003DCF8A /* HomeSevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD202A18EF43003DCF8A /* HomeSevice.swift */; }; 09DCCD232A18EFB0003DCF8A /* HomeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD222A18EFB0003DCF8A /* HomeAPI.swift */; }; @@ -202,6 +203,7 @@ 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 = ""; }; + 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = ""; }; 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyMissionResponseDTO.swift; sourceTree = ""; }; 09DCCD202A18EF43003DCF8A /* HomeSevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSevice.swift; sourceTree = ""; }; 09DCCD222A18EFB0003DCF8A /* HomeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAPI.swift; sourceTree = ""; }; @@ -579,6 +581,7 @@ 3B027AA1299C355800BEB65C /* AchievementViewController.swift */, 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */, 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */, + 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */, ); path = ViewControllers; sourceTree = ""; @@ -1298,6 +1301,7 @@ 3B11740D2A4B574B0033DDF3 /* CALayer+.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 */, @@ -1519,7 +1523,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1557,7 +1561,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift new file mode 100644 index 00000000..a638dd51 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift @@ -0,0 +1,64 @@ +// +// DetailAchieveHeaderView.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 11/19/23. +// + +import UIKit + +import SnapKit +import Then + +final class DetailAchieveHeaderView: UICollectionReusableView { + + // MARK: - Properties + + static let identifier = "DetailAchieveHeaderView" + + // MARK: - UI Components + + private let dateLabel = UILabel() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setUI() + setLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +// MARK: - Method + +extension DetailAchieveHeaderView { + + private func setUI() { + + dateLabel.do { + $0.font = .Pretendard(.semiBold, size: 18) + $0.textColor = .gray2 + $0.textAlignment = .center + } + } + + private func setLayout() { + addSubview(dateLabel) + + dateLabel.snp.makeConstraints { + $0.top.equalToSuperview() + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().inset(12) + } + } + + func configure(text: String) { + dateLabel.text = text + } +} From 2a3061fc19eeb44995d2ecf38e1c77ee2880b0a6 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:05:22 +0900 Subject: [PATCH 11/26] =?UTF-8?q?[Fix]=20#196=20-=20=EB=94=94=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=84=B1=EC=B7=A8=20cell=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DetailAchievementCollectionViewCell.swift | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift index c4a392c5..c665491c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift @@ -7,7 +7,10 @@ import UIKit -class DetailAchievementCollectionViewCell: UICollectionViewCell { +import SnapKit +import Then + +final class DetailAchievementCollectionViewCell: UICollectionViewCell { // MARK: - Properties @@ -16,8 +19,6 @@ class DetailAchievementCollectionViewCell: UICollectionViewCell { // MARK: - UI Components let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) - private let horizontalStackView = UIStackView() - private let emptyView = UIView() let titleLabel = UILabel() private let checkImage = UIImageView() @@ -25,6 +26,7 @@ class DetailAchievementCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) + setUI() setLayout() } @@ -37,55 +39,57 @@ class DetailAchievementCollectionViewCell: UICollectionViewCell { // MARK: - Methods extension DetailAchievementCollectionViewCell { + private func setUI() { + contentView.backgroundColor = .clear tagLabel.do { $0.layer.backgroundColor = UIColor.bg?.cgColor $0.font = .Pretendard(.medium, size: 14) $0.textColor = .gray1 - $0.layer.cornerRadius = 10 + $0.layer.cornerRadius = 25/2 } - - horizontalStackView.do { - $0.addArrangedSubviews(titleLabel, emptyView, checkImage) - $0.axis = .horizontal - } - + titleLabel.do { $0.font = .Pretendard(.semiBold, size: 16) $0.textColor = .gray2 - $0.numberOfLines = 0 + $0.numberOfLines = 1 $0.textAlignment = .left } + checkImage.do { $0.image = .icChecked } } + private func setLayout() { - addSubviews(tagLabel, horizontalStackView) - + contentView.addSubviews(tagLabel, titleLabel, checkImage) + tagLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(22) - $0.leading.equalToSuperview().offset(29) + $0.leading.equalToSuperview().inset(28) } - - horizontalStackView.snp.makeConstraints { + + titleLabel.snp.makeConstraints { $0.top.equalTo(tagLabel.snp.bottom).offset(7) $0.leading.equalToSuperview().inset(28) - $0.trailing.equalToSuperview() - $0.bottom.equalToSuperview().inset(24) + $0.trailing.equalToSuperview().inset(50) } checkImage.snp.makeConstraints { - $0.trailing.equalToSuperview() + $0.centerY.equalTo(titleLabel.snp.centerY) $0.size.equalTo(21) + $0.trailing.equalToSuperview().inset(28) + $0.bottom.equalToSuperview().inset(24) } } func configure(model: DailyMissionResponseDTO) { - tagLabel.text = model.title - titleLabel.text = model.situationName + tagLabel.text = model.situationName + titleLabel.text = model.title + titleLabel.lineBreakMode = .byTruncatingTail + switch model.completionStatus { case .CHECKED: checkImage.isHidden = false case .UNCHECKED: checkImage.isHidden = true From 5345658c984f79c5a8142e5b911fc35b00837d9d Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:05:52 +0900 Subject: [PATCH 12/26] =?UTF-8?q?[Refactor]=20#196=20-=20=EB=94=94?= =?UTF-8?q?=ED=85=8C=EC=9D=BC=20=EC=84=B1=EC=B7=A8=20VC=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 --- .../DetailAchievementViewController.swift | 145 +++++++++--------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 924729f4..6119fe79 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -14,38 +14,44 @@ final class DetailAchievementViewController: UIViewController { // MARK: - Properties - var missionList: [DailyMissionResponseDTO] = [] - private var mission: String? - private var goal: String? - var selectedDate: Date? + typealias CellRegistration = UICollectionView.CellRegistration + typealias HeaderRegistration = UICollectionView.SupplementaryRegistration + typealias Item = DailyMissionResponseDTO + typealias DataSource = UICollectionViewDiffableDataSource + typealias SnapShot = NSDiffableDataSourceSnapshot + enum Section: Int, Hashable { case main } - typealias Item = AnyHashable - var dataSource: UICollectionViewDiffableDataSource! = nil + + var selectedDate: Date? + + private var dataSource: DataSource? + private lazy var safeArea = self.view.safeAreaLayoutGuide // MARK: - UI Components - private let backGroundView = UIView() - private let dateLabel = UILabel() - private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) // 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)) + requestDetailAPI(date: Utils.dateFormatterString(format: "YYYY-MM-dd", + date: selectedDate)) } } + override func viewDidLoad() { super.viewDidLoad() - register() + setUI() setLayout() setupDataSource() - reloadData() + setSnapShot() } override func touchesBegan(_ touches: Set, with event: UIEvent?) { @@ -53,7 +59,7 @@ final class DetailAchievementViewController: UIViewController { let touch = touches.first! let location = touch.location(in: self.view) - if !backGroundView.frame.contains(location) { + if !collectionView.frame.contains(location) { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.closeDailyMissionModal) self.dismiss(animated: true) } @@ -63,66 +69,59 @@ final class DetailAchievementViewController: UIViewController { // MARK: - Methods extension DetailAchievementViewController { - private func register() { - collectionView.register(DetailAchievementCollectionViewCell.self, forCellWithReuseIdentifier: DetailAchievementCollectionViewCell.identifier) - } - + private func setUI() { view.backgroundColor = .black.withAlphaComponent(0.6) - - backGroundView.do { + + collectionView.do { + $0.collectionViewLayout = layout() $0.layer.cornerRadius = 15 $0.backgroundColor = .white - $0.isUserInteractionEnabled = false - } - dateLabel.do { - if let selectedDate = selectedDate { - $0.text = Utils.dateFormatterString(format: "YYYY년 MM월 dd일", date: selectedDate) - } - $0.font = .Pretendard(.semiBold, size: 18) - $0.textColor = .gray2 - $0.textAlignment = .center - } - collectionView.do { - $0.backgroundColor = .clear $0.bounces = false $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] } } private func setLayout() { - view.addSubview(backGroundView) - backGroundView.addSubviews(dateLabel, collectionView) - - backGroundView.snp.makeConstraints { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { $0.center.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea).inset(15) $0.height.equalTo(getDeviceWidth()*1.1) } - dateLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(26) - $0.centerX.equalToSuperview() - } - collectionView.snp.makeConstraints { - $0.top.equalTo(dateLabel.snp.bottom) - $0.leading.equalToSuperview() - $0.trailing.equalToSuperview().inset(15) - $0.bottom.equalToSuperview() - } } private func setupDataSource() { - dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailAchievementCollectionViewCell.identifier, for: indexPath) as? DetailAchievementCollectionViewCell else { return UICollectionViewCell() } - cell.configure(model: item as! DailyMissionResponseDTO) - return cell + + let cellRegistration = CellRegistration {cell, _, item in + cell.configure(model: 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)) + } + } + + dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, + for: indexPath, + item: item) }) + + dataSource?.supplementaryViewProvider = { collectionView, _, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, + for: indexPath) + } } - private func reloadData() { - var snapShot = NSDiffableDataSourceSnapshot() + private func setSnapShot() { + + var snapShot = SnapShot() defer { - dataSource.apply(snapShot, animatingDifferences: false) + dataSource?.apply(snapShot, animatingDifferences: false) } snapShot.appendSections([.main]) @@ -130,38 +129,38 @@ extension DetailAchievementViewController { } private func updateData(item: [DailyMissionResponseDTO]) { - var snapshot = dataSource.snapshot() + + guard var snapshot = dataSource?.snapshot() else { return } + snapshot.appendItems(item, toSection: .main) - dataSource.apply(snapshot) + dataSource?.apply(snapshot) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.appearDailyMissionModal(total: item.count)) } private func layout() -> UICollectionViewCompositionalLayout { - var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) + + var config = UICollectionLayoutListConfiguration(appearance: .plain) + config.headerMode = .supplementary config.backgroundColor = .clear config.separatorConfiguration.color = .gray5! - let listLayout = UICollectionViewCompositionalLayout.list(using: config) - return listLayout + config.separatorConfiguration.topSeparatorVisibility = .hidden + config.separatorConfiguration.bottomSeparatorInsets = .init(top: 0, + leading: 20, + bottom: 0, + trailing: 20) + + return UICollectionViewCompositionalLayout.list(using: config) } } extension DetailAchievementViewController { - func requestDetailAPI(date: String) { - HomeAPI.shared.getDailyMission(date: date) { [self] result in - switch result { - case let .success(data): - guard let data = data as? [DailyMissionResponseDTO] else { return } - self.missionList = data - updateData(item: missionList) - case .requestErr: - print("requestErr") - case .pathErr: - print("pathErr") - case .serverErr: - print("serverErr") - case .networkFail: - print("networkFail") - } + + private func requestDetailAPI(date: String) { + HomeAPI.shared.getDailyMission(date: date) { response in + guard let response = response else { return } + guard let data = response.data else { return } + self.updateData(item: data) } } } From ec84911ac4389ea3d78de3b9d7128299d337cf32 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 16:06:07 +0900 Subject: [PATCH 13/26] =?UTF-8?q?[Fix]=20#196=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=20=EB=A1=9C=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 --- .../Network/API/Home/HomeAPI.swift | 24 +++++++++++-------- .../ViewControllers/HomeViewController.swift | 23 ++++++------------ 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index f753522b..686e3d62 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -26,16 +26,20 @@ final class HomeAPI { // MARK: - GET - func getDailyMission(date: String, completion: @escaping (NetworkResult) -> Void) { - homeProvider.request(.dailyMission(date: date)) { response in - switch response { - case let .success(response): - let statusCode = response.statusCode - let data = response.data - let networkResult = NetworkBase.judgeStatus(by: statusCode, data, [DailyMissionResponseDTO].self) - completion(networkResult) - case let .failure(err): - print(err) + func getDailyMission(date: String, completion: @escaping (GeneralArrayResponse?) -> Void) { + homeProvider.request(.dailyMission(date: date)) { result in + switch result { + case .success(let response): + do { + self.missionDailyData = try response.map(GeneralArrayResponse?.self) + guard let missionDailtData = self.missionDailyData else { return } + completion(missionDailtData) + } catch let err { + print(err.localizedDescription, 500) + } + case .failure(let err): + print(err.localizedDescription) + completion(nil) } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 8d75533a..0d564e56 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -360,23 +360,14 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend // MARK: - Network extension HomeViewController { - - func requestDailyMissionAPI(date: String) { - HomeAPI.shared.getDailyMission(date: date) { [weak self] result in - switch result { - case let .success(data): - guard let data = data as? [DailyMissionResponseDTO] else {return} - self?.missionList = [] - if !data.isEmpty { - self?.missionList = data - } - self?.updateData() - case .pathErr: print("pathErr") - case .serverErr: print("serverErr") - case .networkFail: print("networkFail") - case .requestErr: print("networkFail") - } + func requestDailyMissionAPI(date: String) { + + HomeAPI.shared.getDailyMission(date: date) { [weak self] response in + guard let self, let response = response, let data = response.data else { return } + + self.missionList = data + self.updateData() } } From 9173fdc8263a50b9edcb0fa57675efe67e95f22a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:05:12 +0900 Subject: [PATCH 14/26] =?UTF-8?q?[Chore]=20#195=20-=20=EB=82=B4=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20header=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj | 8 ++++---- ...ollectionReusableView.swift => MyInfoHeaderView.swift} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/{MyInfoHeaderCollectionReusableView.swift => MyInfoHeaderView.swift} (100%) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 92673262..fc3ebb73 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -38,7 +38,7 @@ 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 */; }; - 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift */; }; + 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderView.swift */; }; 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 */; }; @@ -196,7 +196,7 @@ 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 = ""; }; - 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoHeaderCollectionReusableView.swift; sourceTree = ""; }; + 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoHeaderView.swift; sourceTree = ""; }; 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 = ""; }; @@ -424,7 +424,7 @@ 098BFD6029B80137008E80F9 /* Cell */ = { isa = PBXGroup; children = ( - 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift */, + 098BFD5E29B7AECF008E80F9 /* MyInfoHeaderView.swift */, 098BFD5829B7999E008E80F9 /* MyProfileCollectionViewCell.swift */, 098BFD5C29B79CE3008E80F9 /* InfoCollectionViewCell.swift */, ); @@ -1290,7 +1290,7 @@ 3B892ABB2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift in Sources */, 3B14A13B29A694C000F92897 /* UITextView+.swift in Sources */, 093DB0372A146BF900ECA5F6 /* MyInfoURL.swift in Sources */, - 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderCollectionReusableView.swift in Sources */, + 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderView.swift in Sources */, 3B482FA7299EB8FD00BCF424 /* UIViewController+.swift in Sources */, 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */, 6CA208302A1925EE001C4247 /* RecommendActionResponseDTO.swift in Sources */, diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderView.swift similarity index 100% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderCollectionReusableView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyInfoHeaderView.swift From fa21ba41e683ec07e75d4ae7a33c679533e38fda Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:05:36 +0900 Subject: [PATCH 15/26] =?UTF-8?q?[Fix]=20#195=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfo/Cell/InfoCollectionViewCell.swift | 1 + .../MyInfo/Cell/MyProfileCollectionViewCell.swift | 9 +++++---- .../Presentation/MyInfo/Model/MyInfoModel.swift | 4 ++-- .../MyInfo/Viewcontrollers/MyInfoViewController.swift | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift index 96120628..b667cb6d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/InfoCollectionViewCell.swift @@ -27,6 +27,7 @@ final class InfoCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: .zero) + setUI() setLayout() } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift index e0661fce..2bdefe65 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Cell/MyProfileCollectionViewCell.swift @@ -13,9 +13,9 @@ import Then final class MyProfileCollectionViewCell: UICollectionViewCell { // MARK: - Properties - + static let identifier = "MyProfileCollectionViewCell" - + // MARK: - UI Components private let logoImage = UIImageView() @@ -24,13 +24,14 @@ final class MyProfileCollectionViewCell: UICollectionViewCell { private let emailLabel = UILabel() // MARK: - Life Cycle - + override init(frame: CGRect) { super.init(frame: .zero) + setUI() setLayout() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift index 0a1845f4..7396ce45 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Model/MyInfoModel.swift @@ -20,10 +20,10 @@ struct InfoModel: Hashable { static let support: [InfoModel] = [InfoModel(image: .icGuide, title: "낫투두 가이드"), InfoModel(image: .icQuestion1, title: "자주 묻는 질문") - ] + ] static let info: [InfoModel] = [InfoModel(title: "공지사항"), InfoModel(title: "문의하기"), InfoModel(title: "약관 및 정책") - ] + ] static func version() -> [InfoModel] { return [InfoModel(title: "버전 정보 "+(Utils.version ?? "1.0.0"))] } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift index 311e7226..e9c2e971 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift @@ -134,7 +134,7 @@ extension MyInfoViewController { let layout = UICollectionViewCompositionalLayout { sectionIndex, env in - let section = Sections(rawValue: sectionIndex) + guard let section = Sections(rawValue: sectionIndex) else { return nil } switch section { case .profile: return CompositionalLayout.setUpSection(layoutEnvironment: env, @@ -143,7 +143,7 @@ extension MyInfoViewController { case .support, .info: return CompositionalLayout.setUpSection(layoutEnvironment: env, topContentInset: 18) - case .version, .none: + case .version: return CompositionalLayout.setUpSection(layoutEnvironment: env, topContentInset: 18, bottomContentInset: 60) From c3c03ccd985921caa35b1826e69d2e938968e58e Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 19 Nov 2023 17:36:05 +0900 Subject: [PATCH 16/26] =?UTF-8?q?[Fix]=20#196=20-=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=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 6119fe79..c9ed7fd7 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -50,7 +50,7 @@ final class DetailAchievementViewController: UIViewController { setUI() setLayout() - setupDataSource() + setDataSource() setSnapShot() } @@ -92,7 +92,7 @@ extension DetailAchievementViewController { } } - private func setupDataSource() { + private func setDataSource() { let cellRegistration = CellRegistration {cell, _, item in cell.configure(model: item) From 5b51c9dc86d6ea73ed969c57c7dda82a4746c207 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Mon, 20 Nov 2023 00:57:10 +0900 Subject: [PATCH 17/26] =?UTF-8?q?[Fix]=20#195=20-=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Viewcontrollers/MyInfoViewController.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift index e9c2e971..21529966 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift @@ -40,7 +40,7 @@ final class MyInfoViewController: UIViewController { setUI() setLayout() setupDataSource() - setSanpShot() + setSnapShot() } } @@ -114,7 +114,7 @@ extension MyInfoViewController { } } - private func setSanpShot() { + private func setSnapShot() { var snapShot = SnapShot() @@ -162,9 +162,9 @@ extension MyInfoViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { switch indexPath.section { case 0: - ProfileSectionSelection() + profileSectionSelection() case 1: - InfoSectionSelection(for: indexPath, + infoSectionSelection(for: indexPath, events: [.clickGuide, .clickFaq], urls: [.guid, .faq]) case 2: @@ -176,7 +176,7 @@ extension MyInfoViewController: UICollectionViewDelegate { } } - private func ProfileSectionSelection() { + private func profileSectionSelection() { sendAnalyticsEvent(.clickMyInfo) { let nextViewController = MyInfoAccountViewController() @@ -185,7 +185,7 @@ extension MyInfoViewController: UICollectionViewDelegate { } } - private func InfoSectionSelection(for indexPath: IndexPath, + private func infoSectionSelection(for indexPath: IndexPath, events: [AnalyticsEvent.MyInfo], urls: [MyInfoURL]) { guard let item = urls.indices.contains(indexPath.item) ? urls[indexPath.item] : nil, From 6d4feaa990d80769f3bbc2e8c6f0656a283b65c2 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Mon, 20 Nov 2023 00:59:08 +0900 Subject: [PATCH 18/26] =?UTF-8?q?[Fix]=20#195=20-=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyInfo/Viewcontrollers/MyInfoViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift index 21529966..27fc26d9 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfo/Viewcontrollers/MyInfoViewController.swift @@ -168,7 +168,7 @@ extension MyInfoViewController: UICollectionViewDelegate { events: [.clickGuide, .clickFaq], urls: [.guid, .faq]) case 2: - InfoSectionSelection(for: indexPath, + infoSectionSelection(for: indexPath, events: [.clickNotice, .clickQuestion, .clickTerms], urls: [.notice, .question, .service]) default: From 319fdd7ebdd583ffea3cf890e62bc4b26356d214 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 26 Nov 2023 19:40:33 +0900 Subject: [PATCH 19/26] =?UTF-8?q?[Fix]=20#196=20-=20=EB=84=A4=ED=8A=B8?= =?UTF-8?q?=EC=9B=8C=ED=81=AC=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/iOS-NOTTODO/Network/API/Home/HomeAPI.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift index 686e3d62..1e1ada19 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Home/HomeAPI.swift @@ -32,7 +32,7 @@ final class HomeAPI { case .success(let response): do { self.missionDailyData = try response.map(GeneralArrayResponse?.self) - guard let missionDailtData = self.missionDailyData else { return } + guard let missionDailtData = self.missionDailyData else { return completion(nil) } completion(missionDailtData) } catch let err { print(err.localizedDescription, 500) From 008b016dc1d4c7ca728251b0d598b794d6cafde5 Mon Sep 17 00:00:00 2001 From: yungu0010 Date: Thu, 18 Jan 2024 13:48:19 +0900 Subject: [PATCH 20/26] =?UTF-8?q?[Feat]=20#221=20-=20=EB=8B=A4=EC=9D=B4?= =?UTF-8?q?=EC=96=BC=EB=A1=9C=EA=B7=B8=EB=B7=B0=EC=9D=98=20=EC=95=B0?= =?UTF-8?q?=ED=94=8C=EB=A6=AC=ED=8A=9C=EB=93=9C=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj | 12 ++++++------ .../iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift | 4 +++- .../NotificationDialogViewController.swift | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 81370737..ecd64402 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -116,6 +116,7 @@ 3B892ABB2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B892ABA2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift */; }; 3B892ABE2A2FBDDE00A316BC /* AddMissionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B892ABD2A2FBDDE00A316BC /* AddMissionAPI.swift */; }; 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B9532F32A284CC1006510F8 /* ModalProtocol.swift */; }; + 3BB6DA4B2B58E42300EDD2D6 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3BB6DA4A2B58E42300EDD2D6 /* API_KEY.plist */; }; 3BBB6C8F2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BBB6C8E2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift */; }; 3BC19A9329CA1CA800C02803 /* UICollectionViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC19A9229CA1CA800C02803 /* UICollectionViewCell+.swift */; }; 3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1A27129C9AF310088376B /* MissionHistoryModels.swift */; }; @@ -151,7 +152,6 @@ 6CA208362A1957CA001C4247 /* AuthAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA208352A1957CA001C4247 /* AuthAPI.swift */; }; 6CA2083A2A195906001C4247 /* AuthResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA208392A195906001C4247 /* AuthResponseDTO.swift */; }; 6CA2B7BB2A222D2300A9E549 /* ValueOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA2B7BA2A222D2300A9E549 /* ValueOnboardingViewController.swift */; }; - 6CBE702D2A5AA29E00A7EC30 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */; }; 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */ = {isa = PBXBuildFile; fileRef = 6CC54C192A28C3AE00AAD76D /* value.json */; }; 6CD4F8BA29AA493600CCC740 /* RecommendActionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4F8B929AA493600CCC740 /* RecommendActionHeaderView.swift */; }; 6CD4F8BC29AA494300CCC740 /* RecommendActionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4F8BB29AA494300CCC740 /* RecommendActionFooterView.swift */; }; @@ -276,6 +276,7 @@ 3B892ABA2A2FBD4C00A316BC /* RecommendSituationResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendSituationResponseDTO.swift; sourceTree = ""; }; 3B892ABD2A2FBDDE00A316BC /* AddMissionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionAPI.swift; sourceTree = ""; }; 3B9532F32A284CC1006510F8 /* ModalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalProtocol.swift; sourceTree = ""; }; + 3BB6DA4A2B58E42300EDD2D6 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = ""; }; 3BBB6C8E2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewLeftAlignLayout.swift; sourceTree = ""; }; 3BC19A9229CA1CA800C02803 /* UICollectionViewCell+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+.swift"; sourceTree = ""; }; 3BC1A27129C9AF310088376B /* MissionHistoryModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MissionHistoryModels.swift; sourceTree = ""; }; @@ -301,7 +302,6 @@ 6CA208352A1957CA001C4247 /* AuthAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthAPI.swift; sourceTree = ""; }; 6CA208392A195906001C4247 /* AuthResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthResponseDTO.swift; sourceTree = ""; }; 6CA2B7BA2A222D2300A9E549 /* ValueOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValueOnboardingViewController.swift; sourceTree = ""; }; - 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = ""; }; 6CC54C192A28C3AE00AAD76D /* value.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = value.json; sourceTree = ""; }; 6CD4F8B929AA493600CCC740 /* RecommendActionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionHeaderView.swift; sourceTree = ""; }; 6CD4F8BB29AA494300CCC740 /* RecommendActionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionFooterView.swift; sourceTree = ""; }; @@ -769,7 +769,7 @@ 3B027AA6299C359900BEB65C /* Resource */ = { isa = PBXGroup; children = ( - 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */, + 3BB6DA4A2B58E42300EDD2D6 /* API_KEY.plist */, 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */, 3B027A85299C31B600BEB65C /* Info.plist */, 3B027AAA299C35D000BEB65C /* Assets */, @@ -1231,11 +1231,11 @@ 3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */, 3B027A84299C31B600BEB65C /* LaunchScreen.storyboard in Resources */, 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */, + 3BB6DA4B2B58E42300EDD2D6 /* API_KEY.plist in Resources */, 3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */, 6C049A312A595C670085E40B /* logo.mp4 in Resources */, 3B027A81299C31B600BEB65C /* Assets.xcassets in Resources */, 3B146D9A299D079D00B17B62 /* Pretendard-Light.otf in Resources */, - 6CBE702D2A5AA29E00A7EC30 /* API_KEY.plist in Resources */, 3B146D98299D077800B17B62 /* Pretendard-Medium.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1540,7 +1540,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CQJ9UKUU35; GENERATE_INFOPLIST_FILE = YES; @@ -1578,7 +1578,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = CQJ9UKUU35; GENERATE_INFOPLIST_FILE = YES; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift index 25be0f23..a243561b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift @@ -27,6 +27,7 @@ enum AnalyticsEvent { case clickOnboardingNext3(select: [String]) case clickOnboardingNext4 case clickOnboardingNext5 + case clickOnboardingNext6 var name: String { switch self { @@ -35,7 +36,7 @@ enum AnalyticsEvent { case .clickOnboardingNext3: return "click_onboarding_next_3" case .clickOnboardingNext4: return "click_onboarding_next_4" case .clickOnboardingNext5: return "click_onboarding_next_5" - + case .clickOnboardingNext6: return "click_onboarding_next_6" } } @@ -46,6 +47,7 @@ enum AnalyticsEvent { case .clickOnboardingNext3(select: let select): return ["onboard_select": select] case .clickOnboardingNext4: return nil case .clickOnboardingNext5: return nil + case .clickOnboardingNext6: return nil } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index 8ab5de09..f5af2feb 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -183,5 +183,6 @@ extension NotificationDialogViewController { @objc private func buttonTapped() { buttonHandler?() + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext6) } } From b064dfaa92349a6240a08ab28d46bff2c6fb13ba Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 24 Jan 2024 19:06:29 +0900 Subject: [PATCH 21/26] =?UTF-8?q?[Add]=20#226=20-=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=EA=B4=80=EB=A0=A8=20=EC=95=B0=ED=94=8C?= =?UTF-8?q?=EB=A6=AC=ED=8A=9C=EB=93=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift index 25be0f23..ae1fcd0a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/AnalyticsEvent.swift @@ -55,20 +55,25 @@ enum AnalyticsEvent { case viewSignIn case clickSignIn(provider: String) case completeSignIn(provider: String) + case clickAdModalCta + case clickAdModalClose(again: String) var name: String { switch self { case .viewSignIn: return "view_signin" case .clickSignIn: return "click_signin" case .completeSignIn: return "complete_signin" + case .clickAdModalCta: return "click_ad_modal_cta" + case .clickAdModalClose: return "click_ad_modal_close" } } var parameters: [String: Any]? { switch self { - case .viewSignIn: return nil + case .viewSignIn, .clickAdModalCta: return nil case .clickSignIn(provider: let provider ): return ["provider": provider] case .completeSignIn(provider: let provider): return ["provider": provider] + case .clickAdModalClose(again: let again): return ["again": again] } } } From 91acb9a10efbe6149c69facf74124fb9d9fd63b9 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Wed, 24 Jan 2024 19:06:49 +0900 Subject: [PATCH 22/26] =?UTF-8?q?[Fix]=20#226=20-=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EB=A1=9C=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 --- .../iOS-NOTTODO/Global/Enum/DefaultKeys.swift | 2 +- .../Global/Enum/KeychainUtil.swift | 6 ++-- .../CommonNotificationViewController.swift | 13 +++++++-- .../ViewControllers/HomeViewController.swift | 29 ++++++++++--------- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/DefaultKeys.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/DefaultKeys.swift index f9844a91..1ac506d8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/DefaultKeys.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/DefaultKeys.swift @@ -27,5 +27,5 @@ struct DefaultKeys { static let socialToken = "socialToken" static let accessToken = "accessToken" static let fcmToken = "fcmToken" - static let isSelected = "isSelected" + static let isDeprecatedBtnClicked = "isDeprecatedBtnClicked" } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift index b3daa9cd..d5ca9364 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Enum/KeychainUtil.swift @@ -46,10 +46,10 @@ public final class KeychainUtil { UserDefaults.standard.string(forKey: DefaultKeys.appleEmail) ?? "연동된 이메일 정보가 없습니다" } - static func isSelected() -> Bool { - UserDefaults.standard.bool(forKey: DefaultKeys.isSelected) + static func isDeprecatedBtnClicked() -> Bool { + UserDefaults.standard.bool(forKey: DefaultKeys.isDeprecatedBtnClicked) } - + static func removeUserInfo() { if UserDefaults.standard.bool(forKey: DefaultKeys.isAppleLogin) { UserDefaults.standard.removeObject(forKey: DefaultKeys.appleName) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift index 6f76e237..211c4963 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift @@ -13,6 +13,10 @@ import SafariServices final class CommonNotificationViewController: UIViewController { + // MARK: - Property + + var tapCloseButton: (() -> Void)? + // MARK: - UI Components private let backgroundView = UIView() @@ -24,7 +28,7 @@ final class CommonNotificationViewController: UIViewController { private lazy var formButton = UIButton() private lazy var closeButton = UIButton() private lazy var deprecatedButton = UIButton() - + // MARK: - View Life Cycle override func viewDidLoad() { @@ -161,15 +165,18 @@ extension CommonNotificationViewController { @objc func didFormButtonTap() { - + guard let url = URL(string: MyInfoURL.googleForm.url) else { return } let safariView: SFSafariViewController = SFSafariViewController(url: url) safariView.delegate = self self.present(safariView, animated: true, completion: nil) + + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickAdModalCta) } @objc func didCancelButtonTap() { + self.tapCloseButton?() dismissViewController() } @@ -177,7 +184,7 @@ extension CommonNotificationViewController { func didDeprecatedButtonTap() { deprecatedButton.isSelected.toggle() KeychainUtil.setBool(deprecatedButton.isSelected, - forKey: DefaultKeys.isSelected) + forKey: DefaultKeys.isDeprecatedBtnClicked) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index bd19779e..1ca37df8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -23,16 +23,14 @@ final class HomeViewController: UIViewController { private var current: Date? private lazy var safeArea = self.view.safeAreaLayoutGuide - private var isSelected: Bool { - return KeychainUtil.isSelected() - } + private var isSelected: Bool = false + private var didDeprecatedButtonTap: Bool { return KeychainUtil.isDeprecatedBtnClicked() } // MARK: - UI Components private var missionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) private lazy var missionDataSource = HomeDataSource(collectionView: missionCollectionView, missionList: missionList) - private lazy var alertViewContrilelr = CommonNotificationViewController() private let weekCalendar = CalendarView(calendarScope: .week, scrollDirection: .horizontal) private let addButton = UIButton() @@ -40,7 +38,7 @@ final class HomeViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + showPopup(isSelected: isSelected) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.viewHome) @@ -53,7 +51,6 @@ final class HomeViewController: UIViewController { setUI() setLayout() - } } @@ -88,7 +85,7 @@ extension HomeViewController { } private func setLayout() { - + view.addSubviews(weekCalendar, missionCollectionView, addButton) weekCalendar.calendar.select(today) @@ -220,7 +217,7 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickWeeklyDate(date: Utils.dateFormatterString(format: nil, date: date))) } - + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { Utils.dateFormatterString(format: "EEEEEE", date: date) } @@ -238,12 +235,12 @@ 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 percentage = getPercentage(for: date) else { return cell } cell.configure(percent: percentage) - + return cell } } @@ -288,9 +285,9 @@ extension HomeViewController { } private func requestDeleteMission(index: Int, id: Int) { - HomeAPI.shared.deleteMission(id: id) { [weak self] _ in + HomeAPI.shared.deleteMission(id: id) { [weak self] _ in guard let self else { return } - + self.dailyLoadData() self.weeklyLoadData() self.missionDataSource.updateSnapShot(missionList: self.missionList) @@ -356,11 +353,17 @@ extension HomeViewController { extension HomeViewController { private func showPopup(isSelected: Bool) { - if !isSelected { + + if !(isSelected || didDeprecatedButtonTap) { let nextView = CommonNotificationViewController() nextView.modalPresentationStyle = .overFullScreen nextView.modalTransitionStyle = .crossDissolve self.present(nextView, animated: true) + + nextView.tapCloseButton = { + self.isSelected = true + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickAdModalClose(again: self.didDeprecatedButtonTap ? "yes": "no" )) + } } } } From b5a9afb63ce7841994cb2b035860ea98dde4a19b Mon Sep 17 00:00:00 2001 From: hyesuuou <68391767+hyesuuou@users.noreply.github.com> Date: Fri, 26 Jan 2024 01:28:04 +0900 Subject: [PATCH 23/26] =?UTF-8?q?[Feat]=20#225=20-=20=EC=95=B1=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO.xcodeproj/project.pbxproj | 56 +++++- .../iOS-NOTTODO/Application/AppDelegate.swift | 112 +---------- .../Application/SceneDelegate.swift | 3 +- .../iOS-NOTTODO/Global/Literals/Strings.swift | 3 + .../Network/External/FirebaseUtil.swift | 47 +++++ .../UpdateCheckViewController.swift | 180 ++++++++++++++++++ 6 files changed, 285 insertions(+), 116 deletions(-) create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Network/External/FirebaseUtil.swift create mode 100644 iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 81370737..1e141b91 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -62,6 +62,10 @@ 09F6719029CB6AB400708725 /* OnboardingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6718F29CB6AB400708725 /* OnboardingFooterView.swift */; }; 09F6719529CBFCD200708725 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6719429CBFCD200708725 /* GradientView.swift */; }; 09F6719729CC81B500708725 /* DetailAchievementCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F6719629CC81B500708725 /* DetailAchievementCollectionViewCell.swift */; }; + 155E45532B5BB8AC008628E7 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 155E45522B5BB8AC008628E7 /* API_KEY.plist */; }; + 155E45662B5FF089008628E7 /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */; }; + 155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155E45682B5FF2EE008628E7 /* FirebaseUtil.swift */; }; + 155E456D2B62B1A1008628E7 /* UpdateCheckViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155E456C2B62B1A1008628E7 /* UpdateCheckViewController.swift */; }; 3B027A78299C31B500BEB65C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027A77299C31B500BEB65C /* AppDelegate.swift */; }; 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027A79299C31B500BEB65C /* SceneDelegate.swift */; }; 3B027A7C299C31B500BEB65C /* AuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B027A7B299C31B500BEB65C /* AuthViewController.swift */; }; @@ -151,7 +155,6 @@ 6CA208362A1957CA001C4247 /* AuthAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA208352A1957CA001C4247 /* AuthAPI.swift */; }; 6CA2083A2A195906001C4247 /* AuthResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA208392A195906001C4247 /* AuthResponseDTO.swift */; }; 6CA2B7BB2A222D2300A9E549 /* ValueOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA2B7BA2A222D2300A9E549 /* ValueOnboardingViewController.swift */; }; - 6CBE702D2A5AA29E00A7EC30 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */; }; 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */ = {isa = PBXBuildFile; fileRef = 6CC54C192A28C3AE00AAD76D /* value.json */; }; 6CD4F8BA29AA493600CCC740 /* RecommendActionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4F8B929AA493600CCC740 /* RecommendActionHeaderView.swift */; }; 6CD4F8BC29AA494300CCC740 /* RecommendActionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4F8BB29AA494300CCC740 /* RecommendActionFooterView.swift */; }; @@ -224,6 +227,9 @@ 09F6718F29CB6AB400708725 /* OnboardingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFooterView.swift; sourceTree = ""; }; 09F6719429CBFCD200708725 /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; 09F6719629CC81B500708725 /* DetailAchievementCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementCollectionViewCell.swift; sourceTree = ""; }; + 155E45522B5BB8AC008628E7 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = ""; }; + 155E45682B5FF2EE008628E7 /* FirebaseUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseUtil.swift; sourceTree = ""; }; + 155E456C2B62B1A1008628E7 /* UpdateCheckViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCheckViewController.swift; sourceTree = ""; }; 3B027A74299C31B500BEB65C /* iOS-NOTTODO.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS-NOTTODO.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B027A77299C31B500BEB65C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 3B027A79299C31B500BEB65C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -301,7 +307,6 @@ 6CA208352A1957CA001C4247 /* AuthAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthAPI.swift; sourceTree = ""; }; 6CA208392A195906001C4247 /* AuthResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthResponseDTO.swift; sourceTree = ""; }; 6CA2B7BA2A222D2300A9E549 /* ValueOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValueOnboardingViewController.swift; sourceTree = ""; }; - 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = ""; }; 6CC54C192A28C3AE00AAD76D /* value.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = value.json; sourceTree = ""; }; 6CD4F8B929AA493600CCC740 /* RecommendActionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionHeaderView.swift; sourceTree = ""; }; 6CD4F8BB29AA494300CCC740 /* RecommendActionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionFooterView.swift; sourceTree = ""; }; @@ -335,6 +340,7 @@ 6C44127129A35A1000313C3F /* KakaoSDK in Frameworks */, 3B146DA4299D0A8600B17B62 /* Then in Frameworks */, 3B146DA7299D0AA300B17B62 /* Moya in Frameworks */, + 155E45662B5FF089008628E7 /* FirebaseRemoteConfig in Frameworks */, 3B2B59442AEB814B00B4619A /* FirebaseMessaging in Frameworks */, 6C44127729A35A1000313C3F /* KakaoSDKNavi in Frameworks */, 6C44128129A35A1000313C3F /* KakaoSDKUser in Frameworks */, @@ -594,6 +600,37 @@ path = ViewControllers; sourceTree = ""; }; + 155E45642B5FF089008628E7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 155E45672B5FF2DE008628E7 /* External */ = { + isa = PBXGroup; + children = ( + 155E45682B5FF2EE008628E7 /* FirebaseUtil.swift */, + ); + path = External; + sourceTree = ""; + }; + 155E456A2B62B17D008628E7 /* UpdateCheck */ = { + isa = PBXGroup; + children = ( + 155E456B2B62B18F008628E7 /* ViewControllers */, + ); + path = UpdateCheck; + sourceTree = ""; + }; + 155E456B2B62B18F008628E7 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 155E456C2B62B1A1008628E7 /* UpdateCheckViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 3B027A6B299C31B500BEB65C = { isa = PBXGroup; children = ( @@ -601,6 +638,7 @@ 3BDE6157299EDD02001CCEA9 /* .swiftlint.yml */, 3B027A76299C31B500BEB65C /* iOS-NOTTODO */, 3B027A75299C31B500BEB65C /* Products */, + 155E45642B5FF089008628E7 /* Frameworks */, ); sourceTree = ""; }; @@ -681,6 +719,7 @@ 3B027A90299C33CA00BEB65C /* Network */ = { isa = PBXGroup; children = ( + 155E45672B5FF2DE008628E7 /* External */, 093DB0312A1468F100ECA5F6 /* Service */, 093DB02F2A1468CE00ECA5F6 /* DataModel */, 093DB02B2A14687300ECA5F6 /* API */, @@ -692,6 +731,7 @@ 3B027A97299C343D00BEB65C /* Presentation */ = { isa = PBXGroup; children = ( + 155E456A2B62B17D008628E7 /* UpdateCheck */, 3B03D0D32B0F157700302872 /* NotificatoinDialog */, 09F6717E29CAD68100708725 /* Onboarding */, 3B027A9B299C348800BEB65C /* Achievement */, @@ -769,7 +809,7 @@ 3B027AA6299C359900BEB65C /* Resource */ = { isa = PBXGroup; children = ( - 6CBE702C2A5AA29E00A7EC30 /* API_KEY.plist */, + 155E45522B5BB8AC008628E7 /* API_KEY.plist */, 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */, 3B027A85299C31B600BEB65C /* Info.plist */, 3B027AAA299C35D000BEB65C /* Assets */, @@ -1170,6 +1210,7 @@ 0943A9F42A531D0000614761 /* Amplitude */, 09C8602C2AB14B4800C4F4B1 /* FSCalendar */, 3B2B59432AEB814B00B4619A /* FirebaseMessaging */, + 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */, ); productName = "iOS-NOTTODO"; productReference = 3B027A74299C31B500BEB65C /* iOS-NOTTODO.app */; @@ -1231,11 +1272,11 @@ 3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */, 3B027A84299C31B600BEB65C /* LaunchScreen.storyboard in Resources */, 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */, + 155E45532B5BB8AC008628E7 /* API_KEY.plist in Resources */, 3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */, 6C049A312A595C670085E40B /* logo.mp4 in Resources */, 3B027A81299C31B600BEB65C /* Assets.xcassets in Resources */, 3B146D9A299D079D00B17B62 /* Pretendard-Light.otf in Resources */, - 6CBE702D2A5AA29E00A7EC30 /* API_KEY.plist in Resources */, 3B146D98299D077800B17B62 /* Pretendard-Medium.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1330,6 +1371,7 @@ 6CA208302A1925EE001C4247 /* RecommendActionResponseDTO.swift in Sources */, 09F6718629CB26E400708725 /* OnboardingHeaderView.swift in Sources */, 3B11740D2A4B574B0033DDF3 /* CALayer+.swift in Sources */, + 155E456D2B62B1A1008628E7 /* UpdateCheckViewController.swift in Sources */, 3B14A13D29A6FBD300F92897 /* UIView+.swift in Sources */, 09F6719529CBFCD200708725 /* GradientView.swift in Sources */, 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */, @@ -1393,6 +1435,7 @@ 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */, 3B892ABE2A2FBDDE00A316BC /* AddMissionAPI.swift in Sources */, 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */, + 155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */, 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */, 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */, 3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */, @@ -1716,6 +1759,11 @@ package = 09C8602B2AB14B4700C4F4B1 /* XCRemoteSwiftPackageReference "FSCalendar" */; productName = FSCalendar; }; + 155E45652B5FF089008628E7 /* FirebaseRemoteConfig */ = { + isa = XCSwiftPackageProductDependency; + package = 3B2B59422AEB814B00B4619A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseRemoteConfig; + }; 3B146DA0299D0A7A00B17B62 /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = 3B146D9F299D0A7A00B17B62 /* XCRemoteSwiftPackageReference "SnapKit" */; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index 77f8729b..57bed8ea 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -16,6 +16,7 @@ import Amplitude import Firebase import FirebaseMessaging +import FirebaseRemoteConfig @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -27,9 +28,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Amplitude.instance().initializeApiKey(Bundle.main.amplitudeAPIKey) KakaoSDK.initSDK(appKey: Bundle.main.kakaoAPIKey) FirebaseApp.configure() - - checkForUpdate() - + // 메시지 대리자 설정 Messaging.messaging().delegate = self @@ -42,20 +41,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - func skipAuthView() { - // 홈 화면으로 바로 이동 - DispatchQueue.main.async { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first { - let tabBarController = TabBarController() - let navigationController = UINavigationController(rootViewController: tabBarController) - navigationController.isNavigationBarHidden = true - window.rootViewController = navigationController - window.makeKeyAndVisible() - } - } - } - func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken @@ -99,96 +84,3 @@ extension AppDelegate: UNUserNotificationCenterDelegate { completionHandler() } } - -extension AppDelegate { - func checkForUpdate() { - // 앱스토어 버전 - guard let appstoreVersion = getAppstoreVersion() else { return } - - // 현재 설치된 앱의 버전 - guard let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return } - - if compareVersion(userVersion: appVersion, appstoreVersion: appstoreVersion) { - DispatchQueue.main.async { - self.showUpdateAlert() - } - } else { - if KeychainUtil.getAccessToken() != "" { - self.skipAuthView() - print("토큰 유효") - } - } - } - - /// 버전 비교하는 메서드 - func compareVersion(userVersion: String, appstoreVersion: String) -> Bool { - let userMajor = userVersion.split(separator: ".").map {Int($0)!}[0] - let appstoreMajor = appstoreVersion.split(separator: ".").map {Int($0)!}[0] - - if userMajor < appstoreMajor { - return true - } - - let userMinor = userVersion.split(separator: ".").map {Int($0)!}[1] - let appstoreMinor = appstoreVersion.split(separator: ".").map {Int($0)!}[1] - - if userMinor < appstoreMinor { - return true - } - - let userPatch = userVersion.split(separator: ".").map {Int($0)!}[2] - let appstorePatch = appstoreVersion.split(separator: ".").map {Int($0)!}[2] - - if userPatch < appstorePatch { - return true - } - - return false - } - - /// 앱스토어에 배포된 버전 가져오는 메서드 - func getAppstoreVersion() -> String? { - let appleID = Bundle.main.appleId - guard let url = URL(string: "https://itunes.apple.com/lookup?id=\(appleID)"), - let data = try? Data(contentsOf: url), - let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any], - let results = json["results"] as? [[String: Any]], - let appStoreVersion = results[0]["version"] as? String else { - return nil - } - return appStoreVersion - } - - /// 선택 업데이트 경고창 - 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: "https://itunes.apple.com/app/\(Bundle.main.appleId)") { - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: {_ in - if KeychainUtil.getAccessToken() != "" { - self.skipAuthView() - print("토큰 유효") - } - }) - } - } - - let cancelAction = UIAlertAction(title: I18N.later, style: .default) - - alertController.addAction(updateAction) - alertController.addAction(cancelAction) - - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { - if let keyWindow = windowScene.windows.first, - let rootViewController = keyWindow.rootViewController { - rootViewController.present(alertController, animated: true, completion: nil) - } - } - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift index 5751950f..eed04f6c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let window = UIWindow(windowScene: windowScene) window.overrideUserInterfaceStyle = UIUserInterfaceStyle.light - let rootViewController = ValueOnboardingViewController() + let rootViewController = UpdateCheckViewController() let navigationController = UINavigationController(rootViewController: rootViewController) navigationController.isNavigationBarHidden = true window.rootViewController = navigationController @@ -52,7 +52,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } extension SceneDelegate { - func changeRootViewControllerTo(_ viewController: UIViewController) { guard let window = window else { return } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift index e086c32a..f4c002ef 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift @@ -167,6 +167,9 @@ struct I18N { 최신 업데이트가 있습니다. 업데이트하시겠습니까? """ + static func forceUpdateAlert(newVersion: String) -> String { + return "낫투두의 새로운 버전이 있습니다. \(newVersion)버전 으로 업데이트 해주세요." + } static let later = "나중에" static let notiDialogTitle = """ 알림을 허용하면 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/External/FirebaseUtil.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/External/FirebaseUtil.swift new file mode 100644 index 00000000..244f6abb --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/External/FirebaseUtil.swift @@ -0,0 +1,47 @@ +// +// FirebaseUtil.swift +// iOS-NOTTODO +// +// Created by 김혜수 on 1/23/24. +// + +import Foundation + +import FirebaseRemoteConfig + +final class FirebaseUtil { + + static let shared = FirebaseUtil() + + private let config = RemoteConfig.remoteConfig() + + enum RemoteConfigType { + case minimumVersion + } + + init() { + self.setRemoteConfigSetting() + } + + func fetchRemoteConfig(type: RemoteConfigType) async -> String? { + return await withCheckedContinuation { continuation in + config.fetch { [weak self] status, _ in + if status == .success { + self?.config.activate() + guard let version = self?.config["minimum_version"].stringValue, !version.isEmpty else { + continuation.resume(returning: nil) + return + } + continuation.resume(returning: version) + } + } + } + } + + private func setRemoteConfigSetting() { + let setting = RemoteConfigSettings() + setting.minimumFetchInterval = 0 + setting.fetchTimeout = 10 + config.configSettings = setting + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift new file mode 100644 index 00000000..904585ef --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/UpdateCheck/ViewControllers/UpdateCheckViewController.swift @@ -0,0 +1,180 @@ +// +// UpdateCheckViewController.swift +// iOS-NOTTODO +// +// Created by 김혜수 on 1/26/24. +// + +import UIKit + +final class UpdateCheckViewController: UIViewController { + + enum UpdateType { + case optional + case force(newVersion: String) + case none + } + + enum UpdateCheckError: Error { + case versionFetchError + } + + enum Constant { + static let appstoreURL: String = "itms-apps://itunes.apple.com/app/\(Bundle.main.appleId)" + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.setUI() + self.checkUpdate() + } +} + +extension UpdateCheckViewController { + private func setUI() { + self.view.backgroundColor = .ntdBlack + } + + private func checkUpdate() { + Task { + do { + let updateType = try await checkUpdateType() + switch updateType { + case .force(let newVersion): + self.showForceUpdateAlert(newVersion: newVersion) + case .optional: + self.showUpdateAlert() + case .none: + self.changeMainViewController() + } + } catch { + self.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 { + throw UpdateCheckError.versionFetchError + } + + // 현재 설치된 앱의 버전 + guard let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { + throw UpdateCheckError.versionFetchError + } + + // 최소 지원버전 + guard let minimumVersion = await FirebaseUtil.shared.fetchRemoteConfig(type: .minimumVersion) else { + throw UpdateCheckError.versionFetchError + } + + if shouldUpdate(userVersion: appVersion, appstoreVersion: minimumVersion) { + return .force(newVersion: appstoreVersion) + } + + if shouldUpdate(userVersion: appVersion, appstoreVersion: appstoreVersion) { + return .optional + } + + return .none + } + + /// 버전 비교하는 메서드 + private func shouldUpdate(userVersion: String, appstoreVersion: String) -> Bool { + let userMajor = userVersion.split(separator: ".").map {Int($0)!}[0] + let appstoreMajor = appstoreVersion.split(separator: ".").map {Int($0)!}[0] + + if userMajor < appstoreMajor { + return true + } + + let userMinor = userVersion.split(separator: ".").map {Int($0)!}[1] + let appstoreMinor = appstoreVersion.split(separator: ".").map {Int($0)!}[1] + + if userMinor < appstoreMinor { + return true + } + + let userPatch = userVersion.split(separator: ".").map {Int($0)!}[2] + let appstorePatch = appstoreVersion.split(separator: ".").map {Int($0)!}[2] + + if userPatch < appstorePatch { + return true + } + + return false + } + + /// 앱스토어에 배포된 버전 가져오는 메서드 + private func getAppstoreVersion() async throws -> String? { + let appleID = Bundle.main.appleId + guard let url = URL(string: "https://itunes.apple.com/lookup?id=\(appleID)") else { return nil } + let (data, _) = try await URLSession.shared.data(from: url) + let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] + guard let results = json?["results"] as? [[String: Any]], + let appStoreVersion = results[0]["version"] as? String else { + return nil + } + 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 af76f312acdea0fd0cd9005048c294365116f5c7 Mon Sep 17 00:00:00 2001 From: hyesuuou <68391767+hyesuuou@users.noreply.github.com> Date: Fri, 26 Jan 2024 01:28:22 +0900 Subject: [PATCH 24/26] =?UTF-8?q?[Fix]=20#225=20-=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iOS-NOTTODO/Presentation/Auth/AuthViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift index 33af0b2b..416a42a1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift @@ -254,12 +254,12 @@ extension AuthViewController { } func checkNotificationSettings() { - UNUserNotificationCenter.current().getNotificationSettings { settings in + UNUserNotificationCenter.current().getNotificationSettings { [weak self] settings in switch settings.authorizationStatus { case .notDetermined: - self.showNotiDialogView() + self?.showNotiDialogView() default: - break + self?.presentToHomeViewController() } } } From 053b859adc87dd5553f41a637ae85156c3980239 Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sat, 27 Jan 2024 23:53:41 +0900 Subject: [PATCH 25/26] =?UTF-8?q?[Fix]=20#196=20-=20bottomSeparator=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 --- .../DetailAchievementViewController.swift | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 367ae062..697ffb67 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -47,7 +47,7 @@ final class DetailAchievementViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + setUI() setLayout() setDataSource() @@ -69,10 +69,10 @@ final class DetailAchievementViewController: UIViewController { // MARK: - Methods extension DetailAchievementViewController { - + private func setUI() { view.backgroundColor = .black.withAlphaComponent(0.6) - + collectionView.do { $0.collectionViewLayout = layout() $0.layer.cornerRadius = 15 @@ -81,10 +81,10 @@ extension DetailAchievementViewController { $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] } } - + private func setLayout() { view.addSubview(collectionView) - + collectionView.snp.makeConstraints { $0.center.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea).inset(15) @@ -149,6 +149,13 @@ extension DetailAchievementViewController { leading: 20, bottom: 0, trailing: 20) + config.itemSeparatorHandler = { indexPath, config in + var config = 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 + } return UICollectionViewCompositionalLayout.list(using: config) } From 37e1f6aac6839f1356ade4e161734fab32a7934a Mon Sep 17 00:00:00 2001 From: jeongdung-eo Date: Sun, 28 Jan 2024 22:29:52 +0900 Subject: [PATCH 26/26] =?UTF-8?q?[Fix]=20#226=20-=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewControllers/HomeViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 1ca37df8..13df4837 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -23,7 +23,7 @@ final class HomeViewController: UIViewController { private var current: Date? private lazy var safeArea = self.view.safeAreaLayoutGuide - private var isSelected: Bool = false + private var didCloseButtonTap: Bool = false private var didDeprecatedButtonTap: Bool { return KeychainUtil.isDeprecatedBtnClicked() } // MARK: - UI Components @@ -39,7 +39,7 @@ final class HomeViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - showPopup(isSelected: isSelected) + showPopup(isSelected: didCloseButtonTap) AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.viewHome) dailyLoadData() @@ -361,7 +361,7 @@ extension HomeViewController { self.present(nextView, animated: true) nextView.tapCloseButton = { - self.isSelected = true + self.didCloseButtonTap = true AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Login.clickAdModalClose(again: self.didDeprecatedButtonTap ? "yes": "no" )) } }