From 14b2a3f321862f59e4bf5e05f6e3cae45ed834d2 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Wed, 30 Nov 2022 22:13:45 +0900 Subject: [PATCH 01/23] =?UTF-8?q?[feat]=20DayNamePickerView=EC=9D=98=20vie?= =?UTF-8?q?wmodel=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/DayNamePickerViewModel.swift | 44 +++++++++++++++++++ .../Home/ViewModel/EnrollViewModel.swift | 8 ++++ 2 files changed, 52 insertions(+) create mode 100644 DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift create mode 100644 DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift new file mode 100644 index 0000000..f62a202 --- /dev/null +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift @@ -0,0 +1,44 @@ +// +// DayNamePickerViewModel.swift +// DailyQuest +// +// Created by jinwoong Kim on 2022/11/30. +// + +import Foundation + +import RxSwift +import RxRelay + +final class DayNamePickerViewModel { + private var selectedDay = [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false] + private(set) var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false]) + private var disposableBag = DisposeBag() + + init() { /** usecase injection goes here if needed. */} + + struct Input { + let buttonDidClicked: Observable + } + + struct Output { + let switchButtonStatus: Observable<(Int, Bool?)> + } + + func transform(input: Input) -> Output { + print("transform start") + let switchButtonStatus = input + .buttonDidClicked + .map(didClicked(by:)) + .asObservable() + + return Output(switchButtonStatus: switchButtonStatus) + } + + private func didClicked(by index: Int) -> (Int, Bool?) { + selectedDay[index]?.toggle() + selectedDayObservable.accept(selectedDay) + + return (index, selectedDay[index]) + } +} diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift new file mode 100644 index 0000000..4b61919 --- /dev/null +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift @@ -0,0 +1,8 @@ +// +// EnrollViewModel.swift +// DailyQuest +// +// Created by jinwoong Kim on 2022/11/30. +// + +import Foundation From 3a78b8187b863a70fa6bd6dcc64c05384276c925 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Wed, 30 Nov 2022 22:14:04 +0900 Subject: [PATCH 02/23] =?UTF-8?q?[feat]=20EnrollViewController=EC=9D=98=20?= =?UTF-8?q?=EC=84=9C=EB=B8=8C=EB=B7=B0=EB=93=A4=EC=9D=84=20=EA=B4=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=8A=94=20viewmodel=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewModel/EnrollViewModel.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift index 4b61919..41ad81f 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift @@ -6,3 +6,28 @@ // import Foundation + +import RxSwift + +final class EnrollViewModel { + let dayNamePickerViewModel: DayNamePickerViewModel + + init(dayNamePickerViewModel: DayNamePickerViewModel) { + self.dayNamePickerViewModel = dayNamePickerViewModel + } + + struct Input { + let titleDidChanged: Observable + let startDateDidSet: Observable + let endDateDidSet: Observable + let quantityDidSet: Observable + + /** doneButtonDidClickedEvnet */ + } + + struct Output { + let enrollResult: Observable + } + + func transform(input: Input) {} +} From f93ef4f8143e320589588c269622e71465b87814 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Wed, 30 Nov 2022 22:14:20 +0900 Subject: [PATCH 03/23] =?UTF-8?q?[feat]=20DayNamePickerView=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/View/DayNamePickerView.swift | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift index 28a02ad..373b866 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift @@ -11,11 +11,6 @@ import RxSwift import RxCocoa final class DayNamePickerView: UIStackView { - private var selectedDay = [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false] - private(set) var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false]) - - private var disposableBag = DisposeBag() - private lazy var buttons: [UIButton] = { let days = ["S", "M", "T", "W", "T", "F", "S"] @@ -35,8 +30,6 @@ final class DayNamePickerView: UIStackView { super.init(frame: frame) configureUI() - - bind() } required init(coder: NSCoder) { @@ -53,25 +46,28 @@ final class DayNamePickerView: UIStackView { } } - private func bind() { + func bind(with viewModel: DayNamePickerViewModel, disposeBag: DisposeBag) { let taps = buttons.enumerated().map { index, button in button.rx.tap.map { _ in index } } - Observable.from(taps).merge() - .withUnretained(self) - .subscribe(onNext: { (owner, value) in - owner.selectedDay[value]?.toggle() - owner.selectedDayObservable.accept(owner.selectedDay) - - guard let isSelected = owner.selectedDay[value] else { return } + let input = Observable.from(taps).merge() + let output = viewModel + .transform( + input: DayNamePickerViewModel.Input(buttonDidClicked: input) + ) + + output + .switchButtonStatus + .subscribe(onNext: { [weak self] index, isSelected in + guard let isSelected = isSelected else { return } if isSelected { - owner.buttons[value].configuration?.baseBackgroundColor = .maxYellow + self?.buttons[index].configuration?.baseBackgroundColor = .maxYellow } else { - owner.buttons[value].configuration?.baseBackgroundColor = .maxLightYellow + self?.buttons[index].configuration?.baseBackgroundColor = .maxLightYellow } }) - .disposed(by: disposableBag) + .disposed(by: disposeBag) } } From 5b38afbd17a66a6828bb911e671a1b146481d4c7 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Wed, 30 Nov 2022 22:14:35 +0900 Subject: [PATCH 04/23] =?UTF-8?q?[feat]=20EnrollViewController=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DailyQuest/DailyQuest.xcodeproj/project.pbxproj | 8 ++++++++ .../Home/ViewController/EnrollViewController.swift | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj index 51e9cc7..b3bf821 100644 --- a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj +++ b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj @@ -52,6 +52,8 @@ 345687F62937430200CA51E3 /* PlanDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F52937430200CA51E3 /* PlanDatePickerView.swift */; }; 345687F829374D2500CA51E3 /* DayNamePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F729374D2500CA51E3 /* DayNamePickerView.swift */; }; 345687FA2937815900CA51E3 /* QuantityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F92937815900CA51E3 /* QuantityView.swift */; }; + 345687FC293786F100CA51E3 /* DayNamePickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */; }; + 345687FE29378AB900CA51E3 /* EnrollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */; }; 34642AB62925D9E40052FA0E /* UserInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34642AB52925D9E40052FA0E /* UserInfoView.swift */; }; 347D258B292C60F40038FCA2 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D258A292C60F40038FCA2 /* StatusView.swift */; }; 347D258D292C6E220038FCA2 /* MessageBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D258C292C6E220038FCA2 /* MessageBubble.swift */; }; @@ -202,6 +204,8 @@ 345687F52937430200CA51E3 /* PlanDatePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanDatePickerView.swift; sourceTree = ""; }; 345687F729374D2500CA51E3 /* DayNamePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayNamePickerView.swift; sourceTree = ""; }; 345687F92937815900CA51E3 /* QuantityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantityView.swift; sourceTree = ""; }; + 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayNamePickerViewModel.swift; sourceTree = ""; }; + 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollViewModel.swift; sourceTree = ""; }; 34642AB52925D9E40052FA0E /* UserInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoView.swift; sourceTree = ""; }; 347D258A292C60F40038FCA2 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; 347D258C292C6E220038FCA2 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = ""; }; @@ -664,6 +668,8 @@ isa = PBXGroup; children = ( 34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */, + 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */, + 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -1110,6 +1116,7 @@ 34D8360129359C8A001DE9DF /* BrowseRepository.swift in Sources */, 34091552292DE9D1007873A8 /* QuestEntity.swift in Sources */, 342830F6292E1ACA00AE811B /* PlainItemViewModel.swift in Sources */, + 345687FE29378AB900CA51E3 /* EnrollViewModel.swift in Sources */, 34A529D129247880001BAD34 /* Coordinator.swift in Sources */, 34113BEB2934A3B200AB4919 /* LoginViewController.swift in Sources */, 340A724929348B1B00B26AA6 /* AuthUseCase.swift in Sources */, @@ -1181,6 +1188,7 @@ A51F01D029233C510031ECA2 /* UserInfoEntity+Mapping.swift in Sources */, 3499552B29236041007AB99E /* BrowseViewModel.swift in Sources */, 34113BED2934BD3D00AB4919 /* TextFieldForm.swift in Sources */, + 345687FC293786F100CA51E3 /* DayNamePickerViewModel.swift in Sources */, 3449AD5B2922164B00B87619 /* Quest.swift in Sources */, 34283101292E2D7A00AE811B /* ToggleItemViewModel.swift in Sources */, A5AC96E829223F27003B7637 /* RealmQuestsStorage.swift in Sources */, diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index 7422c10..237814f 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -12,6 +12,7 @@ import SnapKit final class EnrollViewController: UIViewController { private var disposableBag = DisposeBag() + private let viewModel = EnrollViewModel(dayNamePickerViewModel: DayNamePickerViewModel()) private lazy var container: UIStackView = { let container = UIStackView() @@ -56,7 +57,10 @@ final class EnrollViewController: UIViewController { configureUI() - daysPicker + bind() + + viewModel + .dayNamePickerViewModel .selectedDayObservable .map { dictionary in dictionary.filter { key, value in @@ -84,4 +88,8 @@ final class EnrollViewController: UIViewController { make.width.equalToSuperview().multipliedBy(0.9) } } + + func bind() { + daysPicker.bind(with: viewModel.dayNamePickerViewModel, disposeBag: disposableBag) + } } From 5f9a45611540bc77117f650d05f8b7ad6e4fe468 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:17:19 +0900 Subject: [PATCH 05/23] =?UTF-8?q?[refactor]=20index=20=EB=A1=9C=EC=A7=81?= =?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 --- .../Domain/UseCases/Home/Protocols/EnrollUseCase.swift | 8 ++++++++ .../Presentation/Home/View/DayNamePickerView.swift | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift new file mode 100644 index 0000000..c0a3dcf --- /dev/null +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift @@ -0,0 +1,8 @@ +// +// EnrollUseCase.swift +// DailyQuest +// +// Created by jinwoong Kim on 2022/12/05. +// + +import Foundation diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift index 373b866..4008ab5 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift @@ -11,7 +11,7 @@ import RxSwift import RxCocoa final class DayNamePickerView: UIStackView { - private lazy var buttons: [UIButton] = { + private(set) lazy var buttons: [UIButton] = { let days = ["S", "M", "T", "W", "T", "F", "S"] return days.map { day in @@ -48,7 +48,7 @@ final class DayNamePickerView: UIStackView { func bind(with viewModel: DayNamePickerViewModel, disposeBag: DisposeBag) { let taps = buttons.enumerated().map { index, button in - button.rx.tap.map { _ in index } + button.rx.tap.map { _ in index + 1 } } let input = Observable.from(taps).merge() @@ -62,9 +62,9 @@ final class DayNamePickerView: UIStackView { .subscribe(onNext: { [weak self] index, isSelected in guard let isSelected = isSelected else { return } if isSelected { - self?.buttons[index].configuration?.baseBackgroundColor = .maxYellow + self?.buttons[index-1].configuration?.baseBackgroundColor = .maxYellow } else { - self?.buttons[index].configuration?.baseBackgroundColor = .maxLightYellow + self?.buttons[index-1].configuration?.baseBackgroundColor = .maxLightYellow } }) .disposed(by: disposeBag) From 064dfa9cc6be7783fcd46bc7679b01a678cf3753 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:17:42 +0900 Subject: [PATCH 06/23] =?UTF-8?q?[refactor]=20PlanDatePickerView=EC=9D=98?= =?UTF-8?q?=20bind=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/View/PlanDatePickerView.swift | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/PlanDatePickerView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/PlanDatePickerView.swift index a3db4f3..488cd46 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/PlanDatePickerView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/PlanDatePickerView.swift @@ -11,7 +11,6 @@ import RxSwift import SnapKit final class PlanDatePickerView: UIView { - private(set) var date = PublishSubject() private var disposableBag = DisposeBag() private lazy var titleLabel: UILabel = { @@ -22,7 +21,7 @@ final class PlanDatePickerView: UIView { return titleLabel }() - private lazy var datePicker: UIDatePicker = { + private(set) lazy var datePicker: UIDatePicker = { let datePicker = UIDatePicker() datePicker.datePickerMode = .date @@ -33,8 +32,6 @@ final class PlanDatePickerView: UIView { super.init(frame: frame) configureUI() - - bind() } required init?(coder: NSCoder) { @@ -56,13 +53,4 @@ final class PlanDatePickerView: UIView { make.edges.equalToSuperview().inset(15) } } - - private func bind() { - datePicker - .rx - .controlEvent(.valueChanged) - .withLatestFrom(datePicker.rx.date) - .bind(to: date) - .disposed(by: disposableBag) - } } From a247094dee5c422521b7bd9386475ccd6b6a2a73 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:18:00 +0900 Subject: [PATCH 07/23] =?UTF-8?q?[refactor]=20QuantityView=EC=9D=98=20bind?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Home/View/QuantityView.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/QuantityView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/QuantityView.swift index f03b97b..e795b0b 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/QuantityView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/QuantityView.swift @@ -7,6 +7,9 @@ import UIKit +import RxSwift +import RxCocoa + final class QuantityView: UIView { private lazy var titleLabel: UILabel = { @@ -17,10 +20,11 @@ final class QuantityView: UIView { return titleLabel }() - private lazy var quantityField: UITextField = { + private(set) lazy var quantityField: UITextField = { let quantityField = UITextField() quantityField.textAlignment = .right quantityField.placeholder = "0" + quantityField.keyboardType = .numberPad return quantityField }() @@ -29,8 +33,6 @@ final class QuantityView: UIView { super.init(frame: frame) configureUI() - - bind() } required init?(coder: NSCoder) { @@ -53,8 +55,4 @@ final class QuantityView: UIView { make.width.equalToSuperview().multipliedBy(0.3) } } - - private func bind() { - - } } From 1a4420b6cbf68e7f5ad575a573593b261bb29ed2 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:18:29 +0900 Subject: [PATCH 08/23] =?UTF-8?q?[refactor]=20ViewModel=EC=9D=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20(=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Home/ViewModel/DayNamePickerViewModel.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift index f62a202..2668602 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift @@ -11,8 +11,8 @@ import RxSwift import RxRelay final class DayNamePickerViewModel { - private var selectedDay = [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false] - private(set) var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [0: false, 1: false, 2: false, 3: false, 4: false, 5: false, 6: false]) + private var selectedDay = [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false] + private(set) var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false]) private var disposableBag = DisposeBag() init() { /** usecase injection goes here if needed. */} @@ -26,7 +26,6 @@ final class DayNamePickerViewModel { } func transform(input: Input) -> Output { - print("transform start") let switchButtonStatus = input .buttonDidClicked .map(didClicked(by:)) From 3587b284eee4755dc553c471493abbb76fa99fc8 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:18:47 +0900 Subject: [PATCH 09/23] =?UTF-8?q?[feat]=20EnrollViewModel=20transform=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewModel/EnrollViewModel.swift | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift index 41ad81f..d2ec767 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift @@ -8,8 +8,11 @@ import Foundation import RxSwift +import RxCocoa final class EnrollViewModel { + + private var disposableBag = DisposeBag() let dayNamePickerViewModel: DayNamePickerViewModel init(dayNamePickerViewModel: DayNamePickerViewModel) { @@ -20,14 +23,60 @@ final class EnrollViewModel { let titleDidChanged: Observable let startDateDidSet: Observable let endDateDidSet: Observable - let quantityDidSet: Observable - - /** doneButtonDidClickedEvnet */ + let quantityDidSet: Observable + let submitButtonDidClicked: Observable } struct Output { + let buttonEnabled: Driver let enrollResult: Observable } - func transform(input: Input) {} + func transform(input: Input) -> Output { + let dates = Observable.combineLatest( + input.startDateDidSet, + input.endDateDidSet, + dayNamePickerViewModel.selectedDayObservable + ) + .compactMap(getDates(start:end:weekday:)) + + let buttonEnabled = Observable.combineLatest( + input.titleDidChanged, + input.quantityDidSet) { title, quantity in + !title.isEmpty && !quantity.isEmpty + } + .asDriver(onErrorJustReturn: false) + + return Output(buttonEnabled: buttonEnabled, enrollResult: .just(true)) + } + + +} + +extension EnrollViewModel { + private func getDates(start: Date, end: Date, weekday: [Int: Bool]) -> [Date]? { + let weekdays = weekday + .compactMap { (key: Int, value: Bool) in + if value { + return key + } else { + return nil + } + } + + var dates: [Date] = [] + var date = start + + while date <= end { + guard let weekday = Calendar(identifier: .gregorian).dateComponents([.weekday], from: date).weekday else { return nil } + if weekdays.contains(weekday) { + dates.append(date) + } + + guard let newDate = Calendar.current.date(byAdding: .day, value: 1, to: date) else { break } + date = newDate + } + + return dates + } } From e40e374b1f4f4fe7482962c44223d8d0a88ab6b8 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:19:06 +0900 Subject: [PATCH 10/23] =?UTF-8?q?[feat]=20EnrollViewController=20bind?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/EnrollViewController.swift | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index 237814f..3f64061 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -8,6 +8,7 @@ import UIKit import RxSwift +import RxCocoa import SnapKit final class EnrollViewController: UIViewController { @@ -51,24 +52,24 @@ final class EnrollViewController: UIViewController { return QuantityView() }() + private lazy var submitButton: UIButton = { + var config = UIButton.Configuration.filled() + config.title = "등록하기" + config.baseBackgroundColor = .maxYellow + config.baseForegroundColor = .maxViolet + + return UIButton(configuration: config) + }() + // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() + quantityView.quantityField.delegate = self + configureUI() bind() - - viewModel - .dayNamePickerViewModel - .selectedDayObservable - .map { dictionary in - dictionary.filter { key, value in - value - } - } - .subscribe(onNext: { print($0) }) - .disposed(by: disposableBag) } private func configureUI() { @@ -79,6 +80,7 @@ final class EnrollViewController: UIViewController { container.addArrangedSubview(endDate) container.addArrangedSubview(daysPicker) container.addArrangedSubview(quantityView) + container.addArrangedSubview(submitButton) view.addSubview(container) @@ -91,5 +93,45 @@ final class EnrollViewController: UIViewController { func bind() { daysPicker.bind(with: viewModel.dayNamePickerViewModel, disposeBag: disposableBag) + + let startDateDidSet = startDate.datePicker.rx + .controlEvent(.valueChanged) + .withLatestFrom(startDate.datePicker.rx.date) + + let endDateDidSet = endDate.datePicker.rx + .controlEvent(.valueChanged) + .withLatestFrom(endDate.datePicker.rx.date) + + let output = viewModel.transform( + input: EnrollViewModel.Input( + titleDidChanged: titleField.rx.text.orEmpty.asObservable(), + startDateDidSet: startDateDidSet, + endDateDidSet: endDateDidSet, + quantityDidSet: quantityView.quantityField.rx.text.orEmpty + .asObservable(), + submitButtonDidClicked: submitButton.rx.tap.asObservable() + ) + ) + + bindSubmitButton(output: output) + } + + private func bindSubmitButton(output: EnrollViewModel.Output) { + output + .buttonEnabled + .drive(submitButton.rx.isEnabled) + .disposed(by: disposableBag) + } +} + +extension EnrollViewController: UITextFieldDelegate { + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + guard CharacterSet(charactersIn: "0123456789").isSuperset(of: CharacterSet(charactersIn: string)) else { + return false + } + guard let oldText = textField.text else { return true } + let text = (oldText as NSString).replacingCharacters(in: range, with: string) + + return text.count < 4 } } From 42c7092f42ecd022820bee847197ac5aa7c16802 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:19:16 +0900 Subject: [PATCH 11/23] [chore] pbx --- DailyQuest/DailyQuest.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj index ef7d1bb..56cc7b1 100644 --- a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj +++ b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 3449AD5B2922164B00B87619 /* Quest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5A2922164B00B87619 /* Quest.swift */; }; 3449AD5D2922197000B87619 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5C2922197000B87619 /* User.swift */; }; 3449AD6029222B3900B87619 /* UserInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5F29222B3900B87619 /* UserInfoCell.swift */; }; + 344A459A293DC495007A3D37 /* EnrollUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A4599293DC495007A3D37 /* EnrollUseCase.swift */; }; 345687F42937329E00CA51E3 /* EnrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F32937329E00CA51E3 /* EnrollViewController.swift */; }; 345687F62937430200CA51E3 /* PlanDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F52937430200CA51E3 /* PlanDatePickerView.swift */; }; 345687F829374D2500CA51E3 /* DayNamePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F729374D2500CA51E3 /* DayNamePickerView.swift */; }; @@ -211,6 +212,7 @@ 3449AD5A2922164B00B87619 /* Quest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quest.swift; sourceTree = ""; }; 3449AD5C2922197000B87619 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 3449AD5F29222B3900B87619 /* UserInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoCell.swift; sourceTree = ""; }; + 344A4599293DC495007A3D37 /* EnrollUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollUseCase.swift; sourceTree = ""; }; 345687F32937329E00CA51E3 /* EnrollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnrollViewController.swift; sourceTree = ""; }; 345687F52937430200CA51E3 /* PlanDatePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanDatePickerView.swift; sourceTree = ""; }; 345687F729374D2500CA51E3 /* DayNamePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayNamePickerView.swift; sourceTree = ""; }; @@ -384,6 +386,7 @@ isa = PBXGroup; children = ( 3416FC87292B54DB00B504C5 /* QuestUseCase.swift */, + 344A4599293DC495007A3D37 /* EnrollUseCase.swift */, ); path = Protocols; sourceTree = ""; @@ -1228,6 +1231,7 @@ 3499552B29236041007AB99E /* BrowseViewModel.swift in Sources */, 34113BED2934BD3D00AB4919 /* TextFieldForm.swift in Sources */, 345687FC293786F100CA51E3 /* DayNamePickerViewModel.swift in Sources */, + 344A459A293DC495007A3D37 /* EnrollUseCase.swift in Sources */, 3449AD5B2922164B00B87619 /* Quest.swift in Sources */, 34283101292E2D7A00AE811B /* ToggleItemViewModel.swift in Sources */, A5AC96E829223F27003B7637 /* RealmQuestsStorage.swift in Sources */, From 03d047421c39175940cb36363f2149a952a073dc Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:38:52 +0900 Subject: [PATCH 12/23] =?UTF-8?q?[refactor]=20DayNamePickerView=20bind=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EC=99=80=20viewmodel=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/View/DayNamePickerView.swift | 24 ----------- .../ViewModel/DayNamePickerViewModel.swift | 43 ------------------- 2 files changed, 67 deletions(-) delete mode 100644 DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift diff --git a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift index 4008ab5..6a45ea2 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/View/DayNamePickerView.swift @@ -45,29 +45,5 @@ final class DayNamePickerView: UIStackView { self.addArrangedSubview(button) } } - - func bind(with viewModel: DayNamePickerViewModel, disposeBag: DisposeBag) { - let taps = buttons.enumerated().map { index, button in - button.rx.tap.map { _ in index + 1 } - } - - let input = Observable.from(taps).merge() - let output = viewModel - .transform( - input: DayNamePickerViewModel.Input(buttonDidClicked: input) - ) - - output - .switchButtonStatus - .subscribe(onNext: { [weak self] index, isSelected in - guard let isSelected = isSelected else { return } - if isSelected { - self?.buttons[index-1].configuration?.baseBackgroundColor = .maxYellow - } else { - self?.buttons[index-1].configuration?.baseBackgroundColor = .maxLightYellow - } - }) - .disposed(by: disposeBag) - } } diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift deleted file mode 100644 index 2668602..0000000 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/DayNamePickerViewModel.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// DayNamePickerViewModel.swift -// DailyQuest -// -// Created by jinwoong Kim on 2022/11/30. -// - -import Foundation - -import RxSwift -import RxRelay - -final class DayNamePickerViewModel { - private var selectedDay = [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false] - private(set) var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false]) - private var disposableBag = DisposeBag() - - init() { /** usecase injection goes here if needed. */} - - struct Input { - let buttonDidClicked: Observable - } - - struct Output { - let switchButtonStatus: Observable<(Int, Bool?)> - } - - func transform(input: Input) -> Output { - let switchButtonStatus = input - .buttonDidClicked - .map(didClicked(by:)) - .asObservable() - - return Output(switchButtonStatus: switchButtonStatus) - } - - private func didClicked(by index: Int) -> (Int, Bool?) { - selectedDay[index]?.toggle() - selectedDayObservable.accept(selectedDay) - - return (index, selectedDay[index]) - } -} From 83ba2d70d76cf528b074fe637b6481ed36b6865f Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:39:05 +0900 Subject: [PATCH 13/23] =?UTF-8?q?[refactor]=20ViewModel=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewModel/EnrollViewModel.swift | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift index d2ec767..61242ee 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift @@ -11,12 +11,13 @@ import RxSwift import RxCocoa final class EnrollViewModel { + private var selectedDay = [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false] + private var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false]) private var disposableBag = DisposeBag() - let dayNamePickerViewModel: DayNamePickerViewModel - init(dayNamePickerViewModel: DayNamePickerViewModel) { - self.dayNamePickerViewModel = dayNamePickerViewModel + init() { + } struct Input { @@ -25,20 +26,25 @@ final class EnrollViewModel { let endDateDidSet: Observable let quantityDidSet: Observable let submitButtonDidClicked: Observable + + let dayButtonDidClicked: Observable } struct Output { let buttonEnabled: Driver let enrollResult: Observable + let dayButtonStatus: Observable<(Int, Bool?)> } func transform(input: Input) -> Output { - let dates = Observable.combineLatest( + Observable.combineLatest( input.startDateDidSet, input.endDateDidSet, - dayNamePickerViewModel.selectedDayObservable + self.selectedDayObservable ) .compactMap(getDates(start:end:weekday:)) + .subscribe(onNext: { print($0) }) + .disposed(by: disposableBag) let buttonEnabled = Observable.combineLatest( input.titleDidChanged, @@ -47,7 +53,14 @@ final class EnrollViewModel { } .asDriver(onErrorJustReturn: false) - return Output(buttonEnabled: buttonEnabled, enrollResult: .just(true)) + let dayButtonStatus = input + .dayButtonDidClicked + .map(didClicked(by:)) + .asObservable() + + return Output(buttonEnabled: buttonEnabled, + enrollResult: .just(true), + dayButtonStatus: dayButtonStatus) } @@ -79,4 +92,11 @@ extension EnrollViewModel { return dates } + + private func didClicked(by index: Int) -> (Int, Bool?) { + selectedDay[index]?.toggle() + selectedDayObservable.accept(selectedDay) + + return (index, selectedDay[index]) + } } From aa29c192b2399a0c7f259d37b4d8a3cb279bfbd4 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:39:43 +0900 Subject: [PATCH 14/23] =?UTF-8?q?[refactor]=20EnrollViewController=20bind?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/EnrollViewController.swift | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index 3f64061..f5250f2 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -13,7 +13,7 @@ import SnapKit final class EnrollViewController: UIViewController { private var disposableBag = DisposeBag() - private let viewModel = EnrollViewModel(dayNamePickerViewModel: DayNamePickerViewModel()) + private let viewModel = EnrollViewModel() private lazy var container: UIStackView = { let container = UIStackView() @@ -92,8 +92,6 @@ final class EnrollViewController: UIViewController { } func bind() { - daysPicker.bind(with: viewModel.dayNamePickerViewModel, disposeBag: disposableBag) - let startDateDidSet = startDate.datePicker.rx .controlEvent(.valueChanged) .withLatestFrom(startDate.datePicker.rx.date) @@ -102,6 +100,11 @@ final class EnrollViewController: UIViewController { .controlEvent(.valueChanged) .withLatestFrom(endDate.datePicker.rx.date) + let taps = daysPicker.buttons.enumerated().map { index, button in + button.rx.tap.map { _ in index + 1 } + } + let dayButtonDidClicked = Observable.from(taps).merge() + let output = viewModel.transform( input: EnrollViewModel.Input( titleDidChanged: titleField.rx.text.orEmpty.asObservable(), @@ -109,11 +112,13 @@ final class EnrollViewController: UIViewController { endDateDidSet: endDateDidSet, quantityDidSet: quantityView.quantityField.rx.text.orEmpty .asObservable(), - submitButtonDidClicked: submitButton.rx.tap.asObservable() + submitButtonDidClicked: submitButton.rx.tap.asObservable(), + dayButtonDidClicked: dayButtonDidClicked ) ) bindSubmitButton(output: output) + bindDayNamePickerView(output: output) } private func bindSubmitButton(output: EnrollViewModel.Output) { @@ -122,6 +127,20 @@ final class EnrollViewController: UIViewController { .drive(submitButton.rx.isEnabled) .disposed(by: disposableBag) } + + private func bindDayNamePickerView(output: EnrollViewModel.Output) { + output + .dayButtonStatus + .bind(onNext: { [weak self] index, isSelected in + guard let isSelected = isSelected else { return } + if isSelected { + self?.daysPicker.buttons[index-1].configuration?.baseBackgroundColor = .maxYellow + } else { + self?.daysPicker.buttons[index-1].configuration?.baseBackgroundColor = .maxLightYellow + } + }) + .disposed(by: disposableBag) + } } extension EnrollViewController: UITextFieldDelegate { From a38eed5be0cc3919a6ac5100c4a17406c54db14b Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 17:39:53 +0900 Subject: [PATCH 15/23] [chore] pbx --- DailyQuest/DailyQuest.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj index 56cc7b1..1449272 100644 --- a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj +++ b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj @@ -54,7 +54,6 @@ 345687F62937430200CA51E3 /* PlanDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F52937430200CA51E3 /* PlanDatePickerView.swift */; }; 345687F829374D2500CA51E3 /* DayNamePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F729374D2500CA51E3 /* DayNamePickerView.swift */; }; 345687FA2937815900CA51E3 /* QuantityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687F92937815900CA51E3 /* QuantityView.swift */; }; - 345687FC293786F100CA51E3 /* DayNamePickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */; }; 345687FE29378AB900CA51E3 /* EnrollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */; }; 34642AB62925D9E40052FA0E /* UserInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34642AB52925D9E40052FA0E /* UserInfoView.swift */; }; 347D258B292C60F40038FCA2 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D258A292C60F40038FCA2 /* StatusView.swift */; }; @@ -217,7 +216,6 @@ 345687F52937430200CA51E3 /* PlanDatePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlanDatePickerView.swift; sourceTree = ""; }; 345687F729374D2500CA51E3 /* DayNamePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayNamePickerView.swift; sourceTree = ""; }; 345687F92937815900CA51E3 /* QuantityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantityView.swift; sourceTree = ""; }; - 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayNamePickerViewModel.swift; sourceTree = ""; }; 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollViewModel.swift; sourceTree = ""; }; 34642AB52925D9E40052FA0E /* UserInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoView.swift; sourceTree = ""; }; 347D258A292C60F40038FCA2 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; @@ -692,7 +690,6 @@ isa = PBXGroup; children = ( 34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */, - 345687FB293786F100CA51E3 /* DayNamePickerViewModel.swift */, 345687FD29378AB900CA51E3 /* EnrollViewModel.swift */, ); path = ViewModel; @@ -1230,7 +1227,6 @@ A51F01D029233C510031ECA2 /* UserInfoEntity+Mapping.swift in Sources */, 3499552B29236041007AB99E /* BrowseViewModel.swift in Sources */, 34113BED2934BD3D00AB4919 /* TextFieldForm.swift in Sources */, - 345687FC293786F100CA51E3 /* DayNamePickerViewModel.swift in Sources */, 344A459A293DC495007A3D37 /* EnrollUseCase.swift in Sources */, 3449AD5B2922164B00B87619 /* Quest.swift in Sources */, 34283101292E2D7A00AE811B /* ToggleItemViewModel.swift in Sources */, From f8c4e1ec10f09cc54ddc7b354a9ab91e8e9a2192 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:02:13 +0900 Subject: [PATCH 16/23] =?UTF-8?q?[feat]=20EnrollUseCase=20protocol=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/UseCases/Home/DefaultEnrollUseCase.swift | 8 ++++++++ .../Domain/UseCases/Home/Protocols/EnrollUseCase.swift | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift new file mode 100644 index 0000000..b980041 --- /dev/null +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift @@ -0,0 +1,8 @@ +// +// DefaultEnrollUseCase.swift +// DailyQuest +// +// Created by jinwoong Kim on 2022/12/05. +// + +import Foundation diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift index c0a3dcf..32f4db4 100644 --- a/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/EnrollUseCase.swift @@ -6,3 +6,9 @@ // import Foundation + +import RxSwift + +protocol EnrollUseCase { + func save(with quests: [Quest]) -> Observable +} From 8bf4c22585394fffb43306b66a4292a54a007aa5 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:02:24 +0900 Subject: [PATCH 17/23] =?UTF-8?q?[feat]=20Enroll=20use=20case=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=B2=B4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UseCases/Home/DefaultEnrollUseCase.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift index b980041..9f40e6a 100644 --- a/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultEnrollUseCase.swift @@ -6,3 +6,25 @@ // import Foundation + +import RxSwift + +final class DefaultEnrollUseCase { + private let questsRepository: QuestsRepository + + init(questsRepository: QuestsRepository) { + self.questsRepository = questsRepository + } +} + +extension DefaultEnrollUseCase: EnrollUseCase { + func save(with quests: [Quest]) -> Observable { + return questsRepository + .save(with: quests) + .map { _ in + true + } + .catchAndReturn(false) + .asObservable() + } +} From c8eabd1920745abab56fccf31fbc3ef8bcef3d46 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:02:34 +0900 Subject: [PATCH 18/23] =?UTF-8?q?[feat]=20DI=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DIContainer/HomeSceneDIContainer.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/DailyQuest/DailyQuest/Application/DIContainer/HomeSceneDIContainer.swift b/DailyQuest/DailyQuest/Application/DIContainer/HomeSceneDIContainer.swift index 9a8cd22..8660b96 100644 --- a/DailyQuest/DailyQuest/Application/DIContainer/HomeSceneDIContainer.swift +++ b/DailyQuest/DailyQuest/Application/DIContainer/HomeSceneDIContainer.swift @@ -21,16 +21,28 @@ final class HomeSceneDIContainer { return DefaultQuestUseCase(questsRepository: makeQuestsRepository()) } + func makeEnrollUseCase() -> EnrollUseCase { + return DefaultEnrollUseCase(questsRepository: makeQuestsRepository()) + } + // MARK: - View Models func makeQuestViewModel() -> QuestViewModel { return QuestViewModel(questUseCase: makeQuestUseCase()) } + func makeEnrollViewModel() -> EnrollViewModel { + return EnrollViewModel(enrollUseCase: makeEnrollUseCase()) + } + // MARK: - View Controller func makeHomeViewController() -> HomeViewController { return HomeViewController.create(with: makeQuestViewModel()) } + func makeEnrollViewController() -> EnrollViewController { + return EnrollViewController.create(with: makeEnrollViewModel()) + } + // MARK: - Flow func makeHomeCoordinator(navigationController: UINavigationController, homeSceneDIContainer: HomeSceneDIContainer) -> HomeCoordinator { From 500324d58fd2da72fbf1284bc77901a7a393ded0 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:02:56 +0900 Subject: [PATCH 19/23] =?UTF-8?q?[refactor]=20EnrollViewController?= =?UTF-8?q?=EB=A5=BC=20DIContainer=EB=A5=BC=20=ED=86=B5=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DailyQuest/Presentation/Home/Flow/HomeCoordinator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/Flow/HomeCoordinator.swift b/DailyQuest/DailyQuest/Presentation/Home/Flow/HomeCoordinator.swift index 1f10a05..ec7b699 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/Flow/HomeCoordinator.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/Flow/HomeCoordinator.swift @@ -49,8 +49,8 @@ final class DefaultHomeCoordinator: HomeCoordinator { } func showAddQuestFlow() { - let addQuestsViewController = EnrollViewController() - navigationController.present(addQuestsViewController, animated: true) + let enrollViewController = homeSceneDIContainer.makeEnrollViewController() + navigationController.present(enrollViewController, animated: true) } func showAddFriendsFlow() { From edf891d997c66a05cdc08d1fb0bdc60e583723ec Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:03:25 +0900 Subject: [PATCH 20/23] =?UTF-8?q?[feat]=20EnrollViewModel=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewModel/EnrollViewModel.swift | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift index 61242ee..ea7ad73 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/EnrollViewModel.swift @@ -14,10 +14,12 @@ final class EnrollViewModel { private var selectedDay = [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false] private var selectedDayObservable = BehaviorRelay<[Int: Bool]>(value: [1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false]) + private let enrollUseCase: EnrollUseCase + private var disposableBag = DisposeBag() - init() { - + init(enrollUseCase: EnrollUseCase) { + self.enrollUseCase = enrollUseCase } struct Input { @@ -37,14 +39,13 @@ final class EnrollViewModel { } func transform(input: Input) -> Output { - Observable.combineLatest( + let dates = Observable.combineLatest( input.startDateDidSet, input.endDateDidSet, self.selectedDayObservable ) .compactMap(getDates(start:end:weekday:)) - .subscribe(onNext: { print($0) }) - .disposed(by: disposableBag) + .asObservable() let buttonEnabled = Observable.combineLatest( input.titleDidChanged, @@ -58,12 +59,20 @@ final class EnrollViewModel { .map(didClicked(by:)) .asObservable() + let enrollResult = input.submitButtonDidClicked + .withLatestFrom(Observable + .combineLatest( + input.titleDidChanged, + dates, + input.quantityDidSet) + ) + .map(createQuests(title:dates:quantity:)) + .flatMap(enrollUseCase.save(with:)) + return Output(buttonEnabled: buttonEnabled, - enrollResult: .just(true), + enrollResult: enrollResult, dayButtonStatus: dayButtonStatus) } - - } extension EnrollViewModel { @@ -99,4 +108,19 @@ extension EnrollViewModel { return (index, selectedDay[index]) } + + private func createQuests(title: String, dates: [Date], quantity: String) -> [Quest] { + let groupID = UUID() + guard let totalCount = Int(quantity) else { return [] } + + return dates.map { date in + Quest(groupId: groupID, + uuid: UUID(), + date: date, + title: title, + currentCount: 0, + totalCount: totalCount + ) + } + } } From a81e49a1eed1111f2568ae616244f1e602c6f8d1 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:04:00 +0900 Subject: [PATCH 21/23] =?UTF-8?q?[feat]=20=ED=80=98=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=95=A1=EC=85=98=EC=9D=98=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 현재는 true, false인지 확인만 합니다... dismiss하는 과정과 뷰 새로고침을 어떻게 처리할지 고민해야할 듯 합니다. --- .../DailyQuest.xcodeproj/project.pbxproj | 4 ++++ .../ViewController/EnrollViewController.swift | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj index 1449272..edc2a09 100644 --- a/DailyQuest/DailyQuest.xcodeproj/project.pbxproj +++ b/DailyQuest/DailyQuest.xcodeproj/project.pbxproj @@ -105,6 +105,7 @@ 34EE0C662935FD7D002BEC23 /* BrowseItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3499552629235D1E007AB99E /* BrowseItemViewModel.swift */; }; 34EE6EB72924C674005AF583 /* QuestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EE6EB62924C674005AF583 /* QuestView.swift */; }; 34EE6EB92924CAA1005AF583 /* QuestViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */; }; + 34FCD366293DE62700E0DC8A /* DefaultEnrollUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34FCD365293DE62700E0DC8A /* DefaultEnrollUseCase.swift */; }; 34FEFB992935EA6D00954A40 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 34FEFB982935EA6D00954A40 /* Kingfisher */; }; 34FF6C5A292B86F8002AFD4D /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 34FF6C46292B8014002AFD4D /* SnapKit-Dynamic */; }; 34FF6C5D292B8B27002AFD4D /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 34FF6C5C292B8B27002AFD4D /* RxCocoa */; }; @@ -262,6 +263,7 @@ 34EE0C632935FD6B002BEC23 /* BrowseViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseViewModelTests.swift; sourceTree = ""; }; 34EE6EB62924C674005AF583 /* QuestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestView.swift; sourceTree = ""; }; 34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestViewModel.swift; sourceTree = ""; }; + 34FCD365293DE62700E0DC8A /* DefaultEnrollUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultEnrollUseCase.swift; sourceTree = ""; }; 9B1CFB3E292B585700CCE97A /* QuestDTO+Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QuestDTO+Mapping.swift"; sourceTree = ""; }; 9BD8CCF22935BC0D00E6EA2F /* DefaultBrowseRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowseRepository.swift; sourceTree = ""; }; 9BD8CCF42935C38300E6EA2F /* UserDTO+Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDTO+Mapping.swift"; sourceTree = ""; }; @@ -376,6 +378,7 @@ children = ( 3416FC86292B54BF00B504C5 /* Protocols */, 3416FC89292B560800B504C5 /* DefaultQuestUseCase.swift */, + 34FCD365293DE62700E0DC8A /* DefaultEnrollUseCase.swift */, ); path = Home; sourceTree = ""; @@ -1230,6 +1233,7 @@ 344A459A293DC495007A3D37 /* EnrollUseCase.swift in Sources */, 3449AD5B2922164B00B87619 /* Quest.swift in Sources */, 34283101292E2D7A00AE811B /* ToggleItemViewModel.swift in Sources */, + 34FCD366293DE62700E0DC8A /* DefaultEnrollUseCase.swift in Sources */, A5AC96E829223F27003B7637 /* RealmQuestsStorage.swift in Sources */, 345687F829374D2500CA51E3 /* DayNamePickerView.swift in Sources */, 349955122923220E007AB99E /* SwiftUIPreview.swift in Sources */, diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index f5250f2..b89fe9e 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -12,8 +12,8 @@ import RxCocoa import SnapKit final class EnrollViewController: UIViewController { + private var viewModel: EnrollViewModel! private var disposableBag = DisposeBag() - private let viewModel = EnrollViewModel() private lazy var container: UIStackView = { let container = UIStackView() @@ -62,6 +62,13 @@ final class EnrollViewController: UIViewController { }() // MARK: - Life Cycle + static func create(with viewModel: EnrollViewModel) -> EnrollViewController { + let vc = EnrollViewController() + vc.viewModel = viewModel + + return vc + } + override func viewDidLoad() { super.viewDidLoad() @@ -92,14 +99,10 @@ final class EnrollViewController: UIViewController { } func bind() { - let startDateDidSet = startDate.datePicker.rx - .controlEvent(.valueChanged) - .withLatestFrom(startDate.datePicker.rx.date) - - let endDateDidSet = endDate.datePicker.rx - .controlEvent(.valueChanged) - .withLatestFrom(endDate.datePicker.rx.date) + let startDateDidSet = startDate.datePicker.rx.date.asObservable() + let endDateDidSet = endDate.datePicker.rx.date.asObservable() + let taps = daysPicker.buttons.enumerated().map { index, button in button.rx.tap.map { _ in index + 1 } } @@ -119,6 +122,9 @@ final class EnrollViewController: UIViewController { bindSubmitButton(output: output) bindDayNamePickerView(output: output) + + output.enrollResult.subscribe(onNext: { print($0) }) + .disposed(by: disposableBag) } private func bindSubmitButton(output: EnrollViewModel.Output) { From 259f54e2b976110baf9458dbd6dccdc0e7f3bbfc Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:24:11 +0900 Subject: [PATCH 22/23] =?UTF-8?q?[refactor]=20bind=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20output=EC=9D=84=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/ViewController/EnrollViewController.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index b89fe9e..66b53f2 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -99,9 +99,11 @@ final class EnrollViewController: UIViewController { } func bind() { + let titleDidChanged = titleField.rx.text.orEmpty.asObservable() let startDateDidSet = startDate.datePicker.rx.date.asObservable() - let endDateDidSet = endDate.datePicker.rx.date.asObservable() + let quantityDidSet = quantityView.quantityField.rx.text.orEmpty.asObservable() + let submitButtonDidClicked = submitButton.rx.tap.asObservable() let taps = daysPicker.buttons.enumerated().map { index, button in button.rx.tap.map { _ in index + 1 } @@ -110,12 +112,11 @@ final class EnrollViewController: UIViewController { let output = viewModel.transform( input: EnrollViewModel.Input( - titleDidChanged: titleField.rx.text.orEmpty.asObservable(), + titleDidChanged: titleDidChanged, startDateDidSet: startDateDidSet, endDateDidSet: endDateDidSet, - quantityDidSet: quantityView.quantityField.rx.text.orEmpty - .asObservable(), - submitButtonDidClicked: submitButton.rx.tap.asObservable(), + quantityDidSet: quantityDidSet, + submitButtonDidClicked: submitButtonDidClicked, dayButtonDidClicked: dayButtonDidClicked ) ) From a4860be6acc0e841a9327054f3a3767acccba1b6 Mon Sep 17 00:00:00 2001 From: jjinwoong Date: Mon, 5 Dec 2022 18:24:11 +0900 Subject: [PATCH 23/23] =?UTF-8?q?[refactor]=20bind=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EC=97=90=EC=84=9C=20output=EC=9D=84=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/EnrollViewController.swift | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift index b89fe9e..8be8677 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/EnrollViewController.swift @@ -99,23 +99,28 @@ final class EnrollViewController: UIViewController { } func bind() { + let titleDidChanged = titleField.rx.text.orEmpty.asObservable() let startDateDidSet = startDate.datePicker.rx.date.asObservable() - let endDateDidSet = endDate.datePicker.rx.date.asObservable() - - let taps = daysPicker.buttons.enumerated().map { index, button in - button.rx.tap.map { _ in index + 1 } - } + let quantityDidSet = quantityView.quantityField.rx.text.orEmpty.asObservable() + let submitButtonDidClicked = submitButton.rx.tap.asObservable() + + let taps = daysPicker + .buttons + .enumerated() + .map { index, button in + button.rx.tap.map { _ in index + 1 } + } + let dayButtonDidClicked = Observable.from(taps).merge() let output = viewModel.transform( input: EnrollViewModel.Input( - titleDidChanged: titleField.rx.text.orEmpty.asObservable(), + titleDidChanged: titleDidChanged, startDateDidSet: startDateDidSet, endDateDidSet: endDateDidSet, - quantityDidSet: quantityView.quantityField.rx.text.orEmpty - .asObservable(), - submitButtonDidClicked: submitButton.rx.tap.asObservable(), + quantityDidSet: quantityDidSet, + submitButtonDidClicked: submitButtonDidClicked, dayButtonDidClicked: dayButtonDidClicked ) )