From c65848d02ee667a7597af6fdb8496bc4ee23af8b Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Thu, 8 Dec 2022 12:04:14 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[feat]=20corpus=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DailyQuest/DailyQuest/Utils/StatusView+.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 DailyQuest/DailyQuest/Utils/StatusView+.swift diff --git a/DailyQuest/DailyQuest/Utils/StatusView+.swift b/DailyQuest/DailyQuest/Utils/StatusView+.swift new file mode 100644 index 0000000..cfd08e7 --- /dev/null +++ b/DailyQuest/DailyQuest/Utils/StatusView+.swift @@ -0,0 +1,18 @@ +// +// StatusView+.swift +// DailyQuest +// +// Created by jinwoong Kim on 2022/12/08. +// + +import Foundation + +extension StatusView { + private var messages: [String] { + ["화이팅", "잘 할 수 있어", "오늘은 공부를 해보자!", "Hello, World!", "🎹🎵🎶🎵🎶"] + } + + func getRandomMessage() -> String { + return messages.randomElement() ?? "Hello,World!" + } +} From d37a469cf58b7f7869ecd33fa21a280abf3b5d3c Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Thu, 8 Dec 2022 12:05:04 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[feat]=20KingFisher=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EB=B2=84=ED=8A=BC=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DailyQuest/DailyQuest/Utils/UIButton+.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/DailyQuest/DailyQuest/Utils/UIButton+.swift b/DailyQuest/DailyQuest/Utils/UIButton+.swift index 6e02ffd..402109d 100644 --- a/DailyQuest/DailyQuest/Utils/UIButton+.swift +++ b/DailyQuest/DailyQuest/Utils/UIButton+.swift @@ -7,6 +7,8 @@ import UIKit +import Kingfisher + extension UIButton.Configuration { public static func maxStyle() -> UIButton.Configuration { var style = UIButton.Configuration.plain() @@ -18,3 +20,22 @@ extension UIButton.Configuration { return style } } + +extension UIButton { + func setImage(with urlString: String) { + ImageCache.default.retrieveImage(forKey: urlString, options: nil) { result in + switch result { + case .success(let value): + if let image = value.image { + self.setImage(image, for: .normal) + } else { + guard let url = URL(string: urlString) else { return } + let resource = ImageResource(downloadURL: url, cacheKey: urlString) + self.kf.setImage(with: resource, for: .normal) + } + case .failure(let error): + print(error) + } + } + } +} From 1278b0eaf806100d01f4258c87a944bfe11b4fc1 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Thu, 8 Dec 2022 12:05:16 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[fix]=20StatusView=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DailyQuest.xcodeproj/project.pbxproj | 4 + .../Presentation/Home/View/StatusView.swift | 82 ++++++++++--------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj index 31ea4be..8d83adf 100644 --- a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj +++ b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ 3416FC96292B5B5400B504C5 /* Quest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5A2922164B00B87619 /* Quest.swift */; }; 3417B1442935DA4A00900454 /* BrowseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3417B1432935DA4A00900454 /* BrowseUseCase.swift */; }; 3417B1462935DA9D00900454 /* DefaultBrowseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3417B1452935DA9D00900454 /* DefaultBrowseUseCase.swift */; }; + 341932E3294185A0003DE808 /* StatusView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341932E2294185A0003DE808 /* StatusView+.swift */; }; 342830A8292E0FAC00AE811B /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342830A7292E0FAC00AE811B /* SettingsCoordinator.swift */; }; 342830AA292E12C700AE811B /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342830A9292E12C700AE811B /* SettingsViewController.swift */; }; 342830F2292E196E00AE811B /* CommonField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342830F1292E196E00AE811B /* CommonField.swift */; }; @@ -253,6 +254,7 @@ 3416FC94292B5AD600B504C5 /* QuestUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestUseCaseTests.swift; sourceTree = ""; }; 3417B1432935DA4A00900454 /* BrowseUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseUseCase.swift; sourceTree = ""; }; 3417B1452935DA9D00900454 /* DefaultBrowseUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowseUseCase.swift; sourceTree = ""; }; + 341932E2294185A0003DE808 /* StatusView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusView+.swift"; sourceTree = ""; }; 342830A7292E0FAC00AE811B /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = ""; }; 342830A9292E12C700AE811B /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 342830F1292E196E00AE811B /* CommonField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonField.swift; sourceTree = ""; }; @@ -660,6 +662,7 @@ 9BED4DE9293FA92900C60631 /* UIImageView+.swift */, 3436FCF9293F2654003575C3 /* Notification+.swift */, 34517FCB2940A92100AB77E9 /* Alertable.swift */, + 341932E2294185A0003DE808 /* StatusView+.swift */, ); path = Utils; sourceTree = ""; @@ -1346,6 +1349,7 @@ 347D258B292C60F40038FCA2 /* StatusView.swift in Sources */, 9BED4DE8293FA01400C60631 /* ProfileViewModel.swift in Sources */, 34642AB62925D9E40052FA0E /* UserInfoView.swift in Sources */, + 341932E3294185A0003DE808 /* StatusView+.swift in Sources */, 34517FCC2940A92100AB77E9 /* Alertable.swift in Sources */, 349955292923600A007AB99E /* BrowseViewController.swift in Sources */, 34113BE82934917500AB4919 /* LoginViewModel.swift in Sources */, diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift index fae715f..3947b0d 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift @@ -6,6 +6,7 @@ // import UIKit + import RxSwift import RxCocoa import SnapKit @@ -14,9 +15,7 @@ final class StatusView: UIView { private var disposableBag = DisposeBag() var profileButtonDidClick = PublishSubject() var userDataFetched = PublishSubject() - - private let messages = ["화이팅", "잘 할 수 있어", "오늘은 공부를 해보자!", "Hello, World!", "🎹🎵🎶🎵🎶"] - + // MARK: - Components private lazy var iconContainer: UIButton = { var config = UIButton.Configuration.plain() @@ -25,51 +24,52 @@ final class StatusView: UIView { iconContainer.imageView?.contentMode = .scaleToFill return iconContainer }() - + private lazy var messageBubble: MessageBubbleLabel = { return MessageBubbleLabel(text: getRandomMessage()) }() - + private lazy var statusLabel: UILabel = { let statusLabel = UILabel() statusLabel.text = "0 / 0" statusLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold) statusLabel.textColor = .maxViolet - + return statusLabel }() - + private lazy var progressBar: UIProgressView = { let progressBar = UIProgressView() progressBar.trackTintColor = .maxLightGrey progressBar.progressTintColor = .maxGreen progressBar.progress = 0.2 - + return progressBar }() - + private lazy var profileButton: UIButton = { - let largeConfig = UIImage.SymbolConfiguration(pointSize: 40, weight: .bold, scale: .large) var config = UIButton.Configuration.plain() config.baseForegroundColor = .maxLightGrey - config.image = UIImage(systemName: "person.crop.circle", - withConfiguration: largeConfig) + config.image = UIImage(systemName: "person.crop.circle") + let button = UIButton(configuration: config) + button.imageView?.contentMode = .scaleAspectFit + return button }() - + // MARK: - Methods override init(frame: CGRect) { super.init(frame: frame) - + configureUI() bind() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + private func configureUI() { addSubview(iconContainer) iconContainer.snp.makeConstraints { make in @@ -77,17 +77,18 @@ final class StatusView: UIView { make.width.equalTo(iconContainer.snp.height) make.top.leading.bottom.equalToSuperview() } - + iconContainer.imageView?.snp.makeConstraints { make in make.width.height.equalToSuperview() + make.top.leading.equalToSuperview() } - + addSubview(messageBubble) messageBubble.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) make.top.equalToSuperview().inset(10) } - + addSubview(progressBar) progressBar.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) @@ -95,47 +96,48 @@ final class StatusView: UIView { make.width.equalToSuperview().multipliedBy(0.5) make.height.equalTo(5) } - + addSubview(profileButton) profileButton.snp.makeConstraints { make in - make.trailing.equalToSuperview() + make.trailing.equalToSuperview().inset(15) make.centerY.equalToSuperview() + make.height.equalTo(75) + make.width.equalTo(75) } - + + profileButton.imageView?.snp.makeConstraints({ make in + make.trailing.equalToSuperview() + make.centerY.equalToSuperview() + make.height.width.equalToSuperview() + }) + addSubview(statusLabel) statusLabel.snp.makeConstraints { make in make.trailing.equalTo(progressBar.snp.trailing) make.bottom.equalTo(progressBar.snp.top) } } - + private func bind() { iconContainer.rx.tap .subscribe(onNext: { [weak self] _ in - guard let self = self else { return } - self.messageBubble.setText(text: self.getRandomMessage()) - }) + guard let self = self else { return } + self.messageBubble.setText(text: self.getRandomMessage()) + }) .disposed(by: disposableBag) - + profileButton.rx.tap .subscribe(onNext: { [weak self] _ in - self?.profileButtonDidClick.onNext(()) - }) + self?.profileButtonDidClick.onNext(()) + }) .disposed(by: disposableBag) - + userDataFetched .asDriver(onErrorJustReturn: User()) .drive(onNext: { [weak self] user in - guard let self = self else { return } - self.profileButton.imageView?.setImage(with: user.profileURL) - }).disposed(by: disposableBag) - - + guard let self = self else { return } + self.profileButton.setImage(with: user.profileURL) + }).disposed(by: disposableBag) } } -extension StatusView { - private func getRandomMessage() -> String { - return messages.randomElement() ?? "Hello,World!" - } -} From 37900730b0c9a7f89b7c26bb9fd95a08863099c7 Mon Sep 17 00:00:00 2001 From: Jeonhui Date: Thu, 8 Dec 2022 12:57:47 +0900 Subject: [PATCH 4/7] [feat] fix var -> let calendarUseCase --- .../DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift index 3e695a6..a3ddd22 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift @@ -13,7 +13,7 @@ import RxCocoa final class HomeViewModel { private let userUseCase: UserUseCase private let questUseCase: QuestUseCase - private var calendarUseCase: CalendarUseCase + private let calendarUseCase: CalendarUseCase private var currentDate = Date() init(userUseCase: UserUseCase, questUseCase: QuestUseCase, calendarUseCase: CalendarUseCase) { From 58d317ca07a56374f9a01a47016d9651830a13ce Mon Sep 17 00:00:00 2001 From: Jeonhui Date: Thu, 8 Dec 2022 12:57:57 +0900 Subject: [PATCH 5/7] [feat] change constraint --- .../Presentation/Home/View/StatusView.swift | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift index 3947b0d..7968568 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift @@ -15,7 +15,8 @@ final class StatusView: UIView { private var disposableBag = DisposeBag() var profileButtonDidClick = PublishSubject() var userDataFetched = PublishSubject() - + var questStatus = PublishSubject<(Int, Int)>() + // MARK: - Components private lazy var iconContainer: UIButton = { var config = UIButton.Configuration.plain() @@ -24,52 +25,52 @@ final class StatusView: UIView { iconContainer.imageView?.contentMode = .scaleToFill return iconContainer }() - + private lazy var messageBubble: MessageBubbleLabel = { return MessageBubbleLabel(text: getRandomMessage()) }() - + private lazy var statusLabel: UILabel = { let statusLabel = UILabel() statusLabel.text = "0 / 0" statusLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold) statusLabel.textColor = .maxViolet - + return statusLabel }() - + private lazy var progressBar: UIProgressView = { let progressBar = UIProgressView() progressBar.trackTintColor = .maxLightGrey progressBar.progressTintColor = .maxGreen progressBar.progress = 0.2 - + return progressBar }() - + private lazy var profileButton: UIButton = { var config = UIButton.Configuration.plain() config.baseForegroundColor = .maxLightGrey config.image = UIImage(systemName: "person.crop.circle") - + let button = UIButton(configuration: config) button.imageView?.contentMode = .scaleAspectFit - + button.layer.cornerRadius = 100 return button }() - + // MARK: - Methods override init(frame: CGRect) { super.init(frame: frame) - + configureUI() bind() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + private func configureUI() { addSubview(iconContainer) iconContainer.snp.makeConstraints { make in @@ -77,18 +78,18 @@ final class StatusView: UIView { make.width.equalTo(iconContainer.snp.height) make.top.leading.bottom.equalToSuperview() } - + iconContainer.imageView?.snp.makeConstraints { make in make.width.height.equalToSuperview() make.top.leading.equalToSuperview() } - + addSubview(messageBubble) messageBubble.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) make.top.equalToSuperview().inset(10) } - + addSubview(progressBar) progressBar.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) @@ -96,48 +97,48 @@ final class StatusView: UIView { make.width.equalToSuperview().multipliedBy(0.5) make.height.equalTo(5) } - + addSubview(profileButton) profileButton.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(15) make.centerY.equalToSuperview() - make.height.equalTo(75) - make.width.equalTo(75) + make.height.equalToSuperview().inset(15) + make.width.equalTo(profileButton.snp.height).multipliedBy(1.0 / 1.0) } - + profileButton.imageView?.snp.makeConstraints({ make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() make.height.width.equalToSuperview() }) - + addSubview(statusLabel) statusLabel.snp.makeConstraints { make in make.trailing.equalTo(progressBar.snp.trailing) make.bottom.equalTo(progressBar.snp.top) } } - + private func bind() { iconContainer.rx.tap .subscribe(onNext: { [weak self] _ in - guard let self = self else { return } - self.messageBubble.setText(text: self.getRandomMessage()) - }) + guard let self = self else { return } + self.messageBubble.setText(text: self.getRandomMessage()) + }) .disposed(by: disposableBag) - + profileButton.rx.tap .subscribe(onNext: { [weak self] _ in - self?.profileButtonDidClick.onNext(()) - }) + self?.profileButtonDidClick.onNext(()) + }) .disposed(by: disposableBag) - + userDataFetched .asDriver(onErrorJustReturn: User()) .drive(onNext: { [weak self] user in - guard let self = self else { return } - self.profileButton.setImage(with: user.profileURL) - }).disposed(by: disposableBag) + guard let self = self else { return } + self.profileButton.setImage(with: user.profileURL) + }).disposed(by: disposableBag) } } From 40436892cd58a8587b17c881cdb8d3e25dece012 Mon Sep 17 00:00:00 2001 From: Jeonhui Date: Thu, 8 Dec 2022 14:12:34 +0900 Subject: [PATCH 6/7] [feat] quest Status update & userUpdate Notification --- .../Repositories/DefaultAuthRepository.swift | 4 +- .../Presentation/Home/View/StatusView.swift | 68 +++++----- .../ViewController/HomeViewController.swift | 79 ++++++------ .../Home/ViewModel/HomeViewModel.swift | 121 +++++++++++------- .../DailyQuest/Utils/Notification+.swift | 1 + 5 files changed, 159 insertions(+), 114 deletions(-) diff --git a/DailyQuest/DailyQuest/Data/Repositories/DefaultAuthRepository.swift b/DailyQuest/DailyQuest/Data/Repositories/DefaultAuthRepository.swift index 50a4ffe..025a0e9 100644 --- a/DailyQuest/DailyQuest/Data/Repositories/DefaultAuthRepository.swift +++ b/DailyQuest/DailyQuest/Data/Repositories/DefaultAuthRepository.swift @@ -47,7 +47,7 @@ extension DefaultAuthRepository: AuthRepository { .flatMap(self.persistentQuestsStorage.saveQuests(with:)) .do(onSuccess: { quests in let dates = quests.map { $0.date } - NotificationCenter.default.post(name: .updated, object: dates) + NotificationCenter.default.post(name: .userUpdated, object: dates) }) } .subscribe() @@ -68,7 +68,7 @@ extension DefaultAuthRepository: AuthRepository { } .do(onSuccess: { quests in let dates = quests.map { $0.date } - NotificationCenter.default.post(name: .updated, object: dates) + NotificationCenter.default.post(name: .userUpdated, object: dates) }) .subscribe() .disposed(by: self.disposeBag) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift index 7968568..b13ad11 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift @@ -16,7 +16,7 @@ final class StatusView: UIView { var profileButtonDidClick = PublishSubject() var userDataFetched = PublishSubject() var questStatus = PublishSubject<(Int, Int)>() - + // MARK: - Components private lazy var iconContainer: UIButton = { var config = UIButton.Configuration.plain() @@ -25,52 +25,52 @@ final class StatusView: UIView { iconContainer.imageView?.contentMode = .scaleToFill return iconContainer }() - + private lazy var messageBubble: MessageBubbleLabel = { return MessageBubbleLabel(text: getRandomMessage()) }() - + private lazy var statusLabel: UILabel = { let statusLabel = UILabel() statusLabel.text = "0 / 0" statusLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold) statusLabel.textColor = .maxViolet - + return statusLabel }() - + private lazy var progressBar: UIProgressView = { let progressBar = UIProgressView() progressBar.trackTintColor = .maxLightGrey progressBar.progressTintColor = .maxGreen progressBar.progress = 0.2 - + return progressBar }() - + private lazy var profileButton: UIButton = { var config = UIButton.Configuration.plain() config.baseForegroundColor = .maxLightGrey config.image = UIImage(systemName: "person.crop.circle") - + let button = UIButton(configuration: config) button.imageView?.contentMode = .scaleAspectFit button.layer.cornerRadius = 100 return button }() - + // MARK: - Methods override init(frame: CGRect) { super.init(frame: frame) - + configureUI() bind() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + private func configureUI() { addSubview(iconContainer) iconContainer.snp.makeConstraints { make in @@ -78,18 +78,18 @@ final class StatusView: UIView { make.width.equalTo(iconContainer.snp.height) make.top.leading.bottom.equalToSuperview() } - + iconContainer.imageView?.snp.makeConstraints { make in make.width.height.equalToSuperview() make.top.leading.equalToSuperview() } - + addSubview(messageBubble) messageBubble.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) make.top.equalToSuperview().inset(10) } - + addSubview(progressBar) progressBar.snp.makeConstraints { make in make.leading.equalTo(iconContainer.snp.trailing) @@ -97,7 +97,7 @@ final class StatusView: UIView { make.width.equalToSuperview().multipliedBy(0.5) make.height.equalTo(5) } - + addSubview(profileButton) profileButton.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(15) @@ -105,40 +105,50 @@ final class StatusView: UIView { make.height.equalToSuperview().inset(15) make.width.equalTo(profileButton.snp.height).multipliedBy(1.0 / 1.0) } - + profileButton.imageView?.snp.makeConstraints({ make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() make.height.width.equalToSuperview() }) - + addSubview(statusLabel) statusLabel.snp.makeConstraints { make in make.trailing.equalTo(progressBar.snp.trailing) make.bottom.equalTo(progressBar.snp.top) } } - + private func bind() { iconContainer.rx.tap .subscribe(onNext: { [weak self] _ in - guard let self = self else { return } - self.messageBubble.setText(text: self.getRandomMessage()) - }) + guard let self = self else { return } + self.messageBubble.setText(text: self.getRandomMessage()) + }) .disposed(by: disposableBag) - + profileButton.rx.tap .subscribe(onNext: { [weak self] _ in - self?.profileButtonDidClick.onNext(()) - }) + self?.profileButtonDidClick.onNext(()) + }) .disposed(by: disposableBag) - + userDataFetched .asDriver(onErrorJustReturn: User()) .drive(onNext: { [weak self] user in - guard let self = self else { return } - self.profileButton.setImage(with: user.profileURL) - }).disposed(by: disposableBag) + guard let self = self else { return } + self.profileButton.setImage(with: user.profileURL) + }).disposed(by: disposableBag) + + questStatus + .asDriver(onErrorJustReturn: (0, 0)) + .drive(onNext: { [weak self] (currentState: Int, totalState: Int) in + guard let self = self else { return } + self.statusLabel.text = "\(currentState)/\(totalState)" + let progressValue = totalState >= 0 ? (Float(currentState) / Float(totalState)) : 0.0 + self.progressBar.setProgress(progressValue, animated: true) + }) + .disposed(by: disposableBag) } } diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift index 3281bdf..9b24f4e 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift @@ -52,12 +52,12 @@ final class HomeViewController: UIViewController { private lazy var questViewHeader: QuestViewHeader = { return QuestViewHeader() }() - + private lazy var emptySpace: UIImageView = { let emptySpace = UIImageView() emptySpace.image = UIImage(named: "NoMoreQuests") emptySpace.isHidden = true - + return emptySpace }() @@ -88,7 +88,7 @@ final class HomeViewController: UIViewController { stackView.addArrangedSubview(calendarView) stackView.addArrangedSubview(questView) stackView.addArrangedSubview(emptySpace) - + scrollView.addSubview(stackView) view.addSubview(scrollView) @@ -109,7 +109,7 @@ final class HomeViewController: UIViewController { calendarView.snp.makeConstraints { make in make.height.equalTo(calendarView.snp.width).multipliedBy(1.4) } - + emptySpace.snp.makeConstraints { make in make.width.equalToSuperview() make.height.equalTo(150) @@ -124,31 +124,31 @@ final class HomeViewController: UIViewController { .rx .didEndDecelerating .map { [weak self] in - guard let indexPath = self?.calendarView - .monthCollectionView - .indexPathsForVisibleItems - .first + guard let indexPath = self?.calendarView + .monthCollectionView + .indexPathsForVisibleItems + .first else { - return CalendarView.ScrollDirection.none - } - - if indexPath.section > 1 { - return CalendarView.ScrollDirection.next - } else if indexPath.section < 1 { - return CalendarView.ScrollDirection.prev - } else { - return CalendarView.ScrollDirection.none + return CalendarView.ScrollDirection.none + } + + if indexPath.section > 1 { + return CalendarView.ScrollDirection.next + } else if indexPath.section < 1 { + return CalendarView.ScrollDirection.prev + } else { + return CalendarView.ScrollDirection.none + } } - } - + let daySelected = calendarView .monthCollectionView .rx .itemSelected .compactMap(calendarView.dataSource.itemIdentifier(for:)) .map { dailyQuestCompletion in - dailyQuestCompletion.day - } + dailyQuestCompletion.day + } .asObservable() let output = viewModel.transform( @@ -161,14 +161,14 @@ final class HomeViewController: UIViewController { ), disposeBag: disposableBag ) - + bindToCalendarView(with: output) bindToQuestHeaderButton() bindToQuestView(with: output) - bindToStatusBarProfileButton(with: output) - bindToStatusBarProfileButtonImage(with: output) + bindToStatusView(with: output) + } - + private func bindToCalendarView(with output: HomeViewModel.Output) { output .displayDays @@ -188,14 +188,14 @@ final class HomeViewController: UIViewController { animated: false) }) .disposed(by: disposableBag) - + output .currentMonth .compactMap({ $0?.toFormat }) .bind(to: calendarView.yearMonthLabel.rx.text) .disposed(by: disposableBag) } - + private func bindToQuestHeaderButton() { questViewHeader .buttonDidClick @@ -218,33 +218,32 @@ final class HomeViewController: UIViewController { .map({ !$0.isEmpty }) .drive(emptySpace.rx.isHidden) .disposed(by: disposableBag) - + output .data .map({ !$0.isEmpty }) .drive(emptySpace.rx.isHidden) .disposed(by: disposableBag) } - - private func bindToStatusBarProfileButton(with output: HomeViewModel.Output) { + + private func bindToStatusView(with output: HomeViewModel.Output) { output .profileTapResult .bind (onNext: needLogIn(result:)) .disposed(by: disposableBag) - } - - - private func bindToStatusBarProfileButtonImage(with output: HomeViewModel.Output) { + output .userData .bind(onNext: { [weak self] user in - guard let self = self else { return } - self.statusView.userDataFetched.onNext(user) - }) + guard let self = self else { return } + self.statusView.userDataFetched.onNext(user) + }) .disposed(by: disposableBag) - } - - private func bindToStatusBarProfileButton() { + + output.questStatus + .drive(onNext: self.statusView.questStatus.onNext) + .disposed(by: disposableBag) + statusView .profileButtonDidClick .bind(onNext: { [weak self] _ in diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift index a3ddd22..6b83b29 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift @@ -15,13 +15,13 @@ final class HomeViewModel { private let questUseCase: QuestUseCase private let calendarUseCase: CalendarUseCase private var currentDate = Date() - + init(userUseCase: UserUseCase, questUseCase: QuestUseCase, calendarUseCase: CalendarUseCase) { self.userUseCase = userUseCase self.questUseCase = questUseCase self.calendarUseCase = calendarUseCase } - + struct Input { let viewDidLoad: Observable let itemDidClicked: Observable @@ -29,17 +29,18 @@ final class HomeViewModel { let dragEventInCalendar: Observable let daySelected: Observable } - + struct Output { let data: Driver<[Quest]> let userData: Observable + let questStatus: Driver<(Int, Int)> let profileTapResult: Observable let currentMonth: Observable let displayDays: Driver<[[DailyQuestCompletion]]> } - + func transform(input: Input, disposeBag: DisposeBag) -> Output { - + let updated = input .itemDidClicked .compactMap { $0.increaseCount() } @@ -47,77 +48,111 @@ final class HomeViewModel { .filter({ $0 }) .compactMap { [weak self] _ in self?.currentDate } .asObservable() - - let notification = NotificationCenter + + let updateNotification = NotificationCenter .default .rx .notification(.updated) + + let userUpdateNotification = NotificationCenter + .default + .rx + .notification(.userUpdated) + + let notification = Observable + .merge( + updateNotification, + userUpdateNotification + ) .compactMap({ $0.object as? [Date] }) .withUnretained(self) .compactMap { owner, dates in - - let result = dates.filter { date in - Calendar.current.isDate(owner.currentDate, inSameDayAs: date) + + let result = dates.filter { date in + Calendar.current.isDate(owner.currentDate, inSameDayAs: date) + } + + return !result.isEmpty ? owner.currentDate : nil } - - return !result.isEmpty ? owner.currentDate : nil - } - + let data = Observable .merge( updated, - input.viewDidLoad, - notification, - input.daySelected + input.viewDidLoad, + notification, + input.daySelected ) .do(onNext: { [weak self] date in self?.currentDate = date - print("fetched ❄️❄️❄️") }) .flatMap(questUseCase.fetch(by:)) - .asDriver(onErrorJustReturn: []) - - let userData = userUseCase - .fetch() - + .asDriver(onErrorJustReturn: []) + + let userNotification = NotificationCenter + .default + .rx + .notification(.userUpdated) + .map { _ in Date() } + + let userData = Observable + .merge( + input.viewDidLoad, + userNotification + ) + .map { _ in Void() } + .flatMap(userUseCase.fetch) + + let questStatus = Observable + .merge( + updated, + input.viewDidLoad, + notification) + .map { _ in Date() } + .flatMap(questUseCase.fetch(by:)) + .map { quests in + (quests.reduce(0) { $0 + ($1.state ? 1 : 0) }, quests.count) + } + .asDriver(onErrorJustReturn: (0, 0)) + let profileTapResult = input .profileButtonDidClicked - .flatMap { [weak self] _ in - guard let self = self else { return Observable.just(false) } - return self.userUseCase.isLoggedIn().take(1) - } - + .flatMap { [weak self] res in + guard let self = self else { return Observable.just(false) } + return self.userUseCase.isLoggedIn().take(1) + } + input .viewDidLoad .subscribe(onNext: { [weak self] _ in - self?.calendarUseCase.setupMonths() - }) + self?.calendarUseCase.setupMonths() + }) .disposed(by: disposeBag) - + input .dragEventInCalendar .subscribe(onNext: { [weak self] direction in - switch direction { - case .prev: - self?.calendarUseCase.fetchLastMontlyCompletion() - case .none: - break - case .next: - self?.calendarUseCase.fetchNextMontlyCompletion() - } - }) + switch direction { + case .prev: + self?.calendarUseCase.fetchLastMontlyCompletion() + case .none: + break + case .next: + self?.calendarUseCase.fetchNextMontlyCompletion() + } + }) .disposed(by: disposeBag) - + let currentMonth = calendarUseCase .currentMonth .asObserver() - + let displayDays = calendarUseCase .completionOfMonths .asDriver(onErrorJustReturn: [[], [], []]) - + return Output(data: data, userData: userData, + questStatus: questStatus, profileTapResult: profileTapResult, currentMonth: currentMonth, displayDays: displayDays) diff --git a/DailyQuest/DailyQuest/Utils/Notification+.swift b/DailyQuest/DailyQuest/Utils/Notification+.swift index df4c79f..4de23b2 100644 --- a/DailyQuest/DailyQuest/Utils/Notification+.swift +++ b/DailyQuest/DailyQuest/Utils/Notification+.swift @@ -9,4 +9,5 @@ import Foundation extension Notification.Name { static let updated = Notification.Name("updated") + static let userUpdated = Notification.Name("userUpdated") } From 4727a84d5497e3bea0fc974bdeefa617cedfec4f Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Thu, 8 Dec 2022 14:31:07 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[fix]=20=EC=A4=91=EB=B3=B5=20present=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 --- .../DailyQuest/Presentation/Home/View/StatusView.swift | 2 +- .../Home/ViewController/HomeViewController.swift | 10 ++-------- .../Presentation/Home/ViewModel/HomeViewModel.swift | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift index b13ad11..cff0c71 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/StatusView.swift @@ -145,7 +145,7 @@ final class StatusView: UIView { .drive(onNext: { [weak self] (currentState: Int, totalState: Int) in guard let self = self else { return } self.statusLabel.text = "\(currentState)/\(totalState)" - let progressValue = totalState >= 0 ? (Float(currentState) / Float(totalState)) : 0.0 + let progressValue = totalState > 0 ? (Float(currentState) / Float(totalState)) : 0.0 self.progressBar.setProgress(progressValue, animated: true) }) .disposed(by: disposableBag) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift index 9b24f4e..7ba0fcd 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift @@ -229,7 +229,8 @@ final class HomeViewController: UIViewController { private func bindToStatusView(with output: HomeViewModel.Output) { output .profileTapResult - .bind (onNext: needLogIn(result:)) + .do(onNext: { _ in print("✅✅") }) + .bind(onNext: needLogIn(result:)) .disposed(by: disposableBag) output @@ -243,13 +244,6 @@ final class HomeViewController: UIViewController { output.questStatus .drive(onNext: self.statusView.questStatus.onNext) .disposed(by: disposableBag) - - statusView - .profileButtonDidClick - .bind(onNext: { [weak self] _ in - self?.coordinatorPublisher.onNext(.showProfileFlow) - }) - .disposed(by: disposableBag) } } diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift index 6b83b29..83411c3 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift @@ -116,7 +116,7 @@ final class HomeViewModel { let profileTapResult = input .profileButtonDidClicked - .flatMap { [weak self] res in + .flatMap { [weak self] in guard let self = self else { return Observable.just(false) } return self.userUseCase.isLoggedIn().take(1) }