From 1f10c53a46829d723eb1f2d89006aaa5976571f2 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Mon, 25 Mar 2024 00:29:44 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[ADD]=20:=20ReactorKit=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Reet-Place/Podfile | 1 + Reet-Place/Podfile.lock | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Reet-Place/Podfile b/Reet-Place/Podfile index 4347e40..fd32ae7 100644 --- a/Reet-Place/Podfile +++ b/Reet-Place/Podfile @@ -13,6 +13,7 @@ target 'Reet-Place' do pod 'RxDataSources', '~> 5.0' # Others + pod 'ReactorKit' pod 'Alamofire' pod 'SnapKit' pod 'Then' diff --git a/Reet-Place/Podfile.lock b/Reet-Place/Podfile.lock index 7bb7d28..09553f5 100644 --- a/Reet-Place/Podfile.lock +++ b/Reet-Place/Podfile.lock @@ -16,6 +16,9 @@ PODS: - NMapsGeometry (1.0.1) - NMapsMap (3.16.2): - NMapsGeometry + - ReactorKit (3.2.0): + - RxSwift (~> 6.0) + - WeakMapTable (~> 1.1) - RxCocoa (6.5.0): - RxRelay (= 6.5.0) - RxSwift (= 6.5.0) @@ -31,6 +34,7 @@ PODS: - RxSwift (6.5.0) - SnapKit (5.6.0) - Then (3.0.0) + - WeakMapTable (1.2.0) DEPENDENCIES: - Alamofire @@ -39,6 +43,7 @@ DEPENDENCIES: - KakaoSDKUser (<= 2.16) - Kingfisher (~> 7.0) - NMapsMap + - ReactorKit - RxCocoa (= 6.5.0) - RxDataSources (~> 5.0) - RxGesture @@ -56,6 +61,7 @@ SPEC REPOS: - Kingfisher - NMapsGeometry - NMapsMap + - ReactorKit - RxCocoa - RxDataSources - RxGesture @@ -63,6 +69,7 @@ SPEC REPOS: - RxSwift - SnapKit - Then + - WeakMapTable SPEC CHECKSUMS: Alamofire: 4e95d97098eacb88856099c4fc79b526a299e48c @@ -73,6 +80,7 @@ SPEC CHECKSUMS: Kingfisher: 6c5449c6450c5239166510ba04afe374a98afc4f NMapsGeometry: 53c573ead66466681cf123f99f698dc8071a4b83 NMapsMap: aaa64717249b06ae82c3a3addb3a01f0e33100ab + ReactorKit: e8b11d6b9c415405f381669b095c154a05b59eca RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b RxDataSources: aa47cc1ed6c500fa0dfecac5c979b723542d79cf RxGesture: f3efb47ed2d26a8082f7b660d4a59970e275a7f8 @@ -80,7 +88,8 @@ SPEC CHECKSUMS: RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 Then: 844265ae87834bbe1147d91d5d41a404da2ec27d + WeakMapTable: 05c694ce8439a7a9ebabb56187287a63c57673d6 -PODFILE CHECKSUM: 6eace9c5f53459f66ad27511b12d74fe7bca04d0 +PODFILE CHECKSUM: 65003312d4883474af61cbcbaac9fb791372e229 COCOAPODS: 1.11.3 From 66cfc4fb9245038172be7ffc61156a76d698b4c1 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:23:31 +0900 Subject: [PATCH 02/14] [RENAME] : TypeInfo -> BookmarkTypeInfo --- .../BookMark/Cell/BookmarkTypeCVC.swift | 2 +- .../BookMark/Model/BookmarkMainModel.swift | 33 ------------------- .../BookMark/Model/BookmarkTypeInfo.swift | 20 +++++++++++ 3 files changed, 21 insertions(+), 34 deletions(-) delete mode 100644 Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkMainModel.swift create mode 100644 Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkTypeInfo.swift diff --git a/Reet-Place/Reet-Place/Screen/BookMark/Cell/BookmarkTypeCVC.swift b/Reet-Place/Reet-Place/Screen/BookMark/Cell/BookmarkTypeCVC.swift index a3ef2b4..1bbf5fc 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/Cell/BookmarkTypeCVC.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/Cell/BookmarkTypeCVC.swift @@ -68,7 +68,7 @@ class BookmarkTypeCVC: BaseCollectionViewCell { // MARK: - Function - func configureData(typeInfo: TypeInfo) { + func configureData(typeInfo: BookmarkTypeInfo) { switch typeInfo.type { case "WANT": titleLabel.text = "BookmarkWishlist".localized diff --git a/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkMainModel.swift b/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkMainModel.swift deleted file mode 100644 index 65d8e2f..0000000 --- a/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkMainModel.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// BookmarkMainModel.swift -// Reet-Place -// -// Created by 김태현 on 2023/04/16. -// - -import Foundation - -struct BookmarkMainModel { - let bookmarkMainInfo: [TypeInfo] -} - -struct TypeInfo { - let type: String - let cnt: Int - let thumbnailUrlString: String -} - -extension BookmarkMainModel { - - static func getMock(_ completion: @escaping(BookmarkMainModel) -> Void) { - completion(BookmarkMainModel(bookmarkMainInfo: [ - TypeInfo(type: "WANT", - cnt: 8, - thumbnailUrlString: "https://picsum.photos/600/400"), - TypeInfo(type: "GONE", - cnt: 4, - thumbnailUrlString: "https://picsum.photos/600/300")]) - ) - } - -} diff --git a/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkTypeInfo.swift b/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkTypeInfo.swift new file mode 100644 index 0000000..e2bb607 --- /dev/null +++ b/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkTypeInfo.swift @@ -0,0 +1,20 @@ +// +// BookmarkTypeInfo.swift +// Reet-Place +// +// Created by 김태현 on 2023/04/16. +// + +import Foundation + +struct BookmarkTypeInfo { + let type: String + let cnt: Int + let thumbnailUrlString: String +} + +extension BookmarkTypeInfo { + static let empty: Self = .init(type: .empty, + cnt: 0, + thumbnailUrlString: .empty) +} From 958471ef33b8172e237aafae03003a8d8ba1e70c Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:23:53 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkVM=20ReactorKit?= =?UTF-8?q?=20-=20Reactor=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BookMark/ViewModel/BookmarkVM.swift | 188 ++++++++++-------- 1 file changed, 100 insertions(+), 88 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkVM.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkVM.swift index 6950cf0..662e050 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkVM.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkVM.swift @@ -7,138 +7,150 @@ import RxCocoa import RxSwift +import ReactorKit -final class BookmarkVM { +final class BookmarkVM: Reactor { - var input = Input() - var output = Output() + // MARK: - Properties - let network: NetworkProtocol = NetworkProvider() - let apiError = PublishSubject() - - var bag = DisposeBag() + enum Action { + case entering + } - struct Input { } + enum Mutation { + case loadBookmarkTypeInfo(wishInfo: BookmarkTypeInfo, historyInfo: BookmarkTypeInfo) + case loadBookmarkWishInfo(wishInfo: BookmarkTypeInfo) + case loadBookmarkHistoryInfo(historyInfo: BookmarkTypeInfo) + case verifyAuthentication(Bool) + case error + } - struct Output { - private let accessToken = BehaviorRelay(value: KeychainManager.shared.read(for: .accessToken)) - - var isAuthenticated: Observable { - accessToken.map { $0 != nil } - } - - var isEmptyBookmark: Observable { - BookmarkAllCnt.map { $0 == 0 } - } - - var BookmarkAllCnt = BehaviorRelay(value: 0) - - var BookmarkWishlistInfo = BehaviorRelay(value: .init(type: "WANT", cnt: 0, thumbnailUrlString: "")) - - var BookmarkHistoryInfo = BehaviorRelay(value: .init(type: "GONE", cnt: 0, thumbnailUrlString: "")) + struct State { + var isAuthenticated: Bool = false + var bookmarkTotalCount: Int = 0 + var bookmarkWishInfo: BookmarkTypeInfo = .empty + var bookmarkHistoryInfo: BookmarkTypeInfo = .empty } + let initialState: State + let network: NetworkProtocol + + + // MARK: - Initializer + init() { - bindInput() - bindOutput() + self.initialState = State() + self.network = NetworkProvider() } - deinit { - bag = DisposeBag() - } + // MARK: - Mutate, Reduce -} - -// MARK: - Input + func mutate(action: Action) -> Observable { + switch action { + case .entering: + return Observable.merge([ + getBookmarkList(type: .want), + getBookmarkList(type: .done), + checkAuthentication() + ]) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .loadBookmarkTypeInfo(let wishInfo, let historyInfo): + newState.bookmarkTotalCount = wishInfo.cnt + historyInfo.cnt + newState.bookmarkWishInfo = wishInfo + newState.bookmarkHistoryInfo = historyInfo + + case .loadBookmarkWishInfo(let wishInfo): + newState.bookmarkTotalCount = state.bookmarkTotalCount + wishInfo.cnt + newState.bookmarkWishInfo = wishInfo + + case .loadBookmarkHistoryInfo(let historyInfo): + newState.bookmarkTotalCount = state.bookmarkTotalCount + historyInfo.cnt + newState.bookmarkHistoryInfo = historyInfo + + case .verifyAuthentication(let isAuthenticated): + newState.isAuthenticated = isAuthenticated + + case .error: + break + } -extension BookmarkVM: Input { - func bindInput() { } + return newState + } + } +// MARK: - Methods -// MARK: - Ouptut - -extension BookmarkVM: Output { - func bindOutput() { } +private extension BookmarkVM { + + /// 로그인 여부 확인 + func checkAuthentication() -> Observable { + let accessToken = KeychainManager.shared.read(for: .accessToken) + let isAuthenticated = accessToken != nil + return Observable.just(.verifyAuthentication(isAuthenticated)) + } } // MARK: - Networking -extension BookmarkVM { - - func getBookmarkMock() { -// BookmarkMainModel.getMock { [weak self] data in -// guard let self = self else { return } -// -// self.output.BookmarkAllCnt.accept(data.bookmarkMainInfo[0].cnt + data.bookmarkMainInfo[1].cnt) -// self.output.BookmarkWishlistInfo.accept(data.bookmarkMainInfo[0]) -// self.output.BookmarkHistoryInfo.accept(data.bookmarkMainInfo[1]) -// } -// - // TODO: - 북마크 종류 별 정보 조회 API 수정 시 복구 - getBookmarkList(type: .want) - getBookmarkList(type: .done) - } +private extension BookmarkVM { + // TODO: - 북마크 종류 별 정보 조회 API 수정 시 교체 연결 /// 서버에 북마크 개수 요청 - func getBookmarkCount() { + func getBookmarkCount() -> Observable { let path = "/api/bookmarks/counts" let endPoint = EndPoint(path: path, httpMethod: .get) - network.request(with: endPoint) - .withUnretained(self) - .subscribe(onNext: { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { case .success(let data): - owner.output.BookmarkAllCnt.accept(data.numOfAll) - owner.output.BookmarkHistoryInfo - .accept(TypeInfo(type: "GONE", - cnt: data.numOfDone, - thumbnailUrlString: "https://picsum.photos/600/400")) - owner.output.BookmarkWishlistInfo - .accept(TypeInfo(type: "WANT", - cnt: data.numOfWant, - thumbnailUrlString: "https://picsum.photos/600/400")) - case .failure(let error): - owner.apiError.onNext(error) + let wishInfo = BookmarkTypeInfo(type: "WANT", cnt: data.numOfWant, thumbnailUrlString: "https://picsum.photos/600/400") + let historyInfo = BookmarkTypeInfo(type: "GONE", cnt: data.numOfDone, thumbnailUrlString: "https://picsum.photos/600/400") + return .loadBookmarkTypeInfo(wishInfo: wishInfo, historyInfo: historyInfo) + case .failure: + return .error } - }) - .disposed(by: bag) + } } // TODO: - 북마크 종류 별 정보 조회 API 수정 시 제거 /// 가고싶어요, 다녀왔어요 북마크를 1 페이지 씩 조회해 존재 여부 확인 - func getBookmarkList(type: BookmarkSearchType) { + func getBookmarkList(type: BookmarkSearchType) -> Observable { let page = 0 let path = "/api/bookmarks?searchType=\(type.rawValue)&page=\(page)&size=10&sort=LATEST" let endPoint = EndPoint(path: path, httpMethod: .get) - network.request(with: endPoint) - .withUnretained(self) - .subscribe(onNext: { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { case .success(let data): let isExist: Bool = !data.content.isEmpty if type == .want { - if isExist { owner.output.BookmarkAllCnt.accept(100) } - owner.output.BookmarkWishlistInfo - .accept(.init(type: "WANT", - cnt: isExist ? 100 : 0, - thumbnailUrlString: "https://picsum.photos/600/400")) + let wishInfo: BookmarkTypeInfo = .init(type: "WANT", + cnt: isExist ? 100 : 0, + thumbnailUrlString: "https://picsum.photos/600/400") + return .loadBookmarkWishInfo(wishInfo: wishInfo) } else if type == .done { - if isExist { owner.output.BookmarkAllCnt.accept(100) } - owner.output.BookmarkHistoryInfo - .accept(.init(type: "GONE", - cnt: isExist ? 100 : 0, - thumbnailUrlString: "https://picsum.photos/600/300")) + let historyInfo: BookmarkTypeInfo = .init(type: "GONE", + cnt: isExist ? 100 : 0, + thumbnailUrlString: "https://picsum.photos/600/400") + return .loadBookmarkHistoryInfo(historyInfo: historyInfo) + } else { + return .error } - case .failure(let error): - owner.apiError.onNext(error) + case .failure: + return .error } - }) - .disposed(by: bag) + } } } From d7b13353396f28b6ce6f5c75866c7c2eb9e65af4 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:24:44 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkVC=20ReactorKit?= =?UTF-8?q?=20-=20View=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reactor 연결 --- .../BookMark/ViewController/BookmarkVC.swift | 148 ++++++++---------- 1 file changed, 63 insertions(+), 85 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkVC.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkVC.swift index 2ded5a0..5e6a536 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkVC.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkVC.swift @@ -9,13 +9,14 @@ import UIKit import RxSwift import RxCocoa +import ReactorKit import Then import SnapKit import Kingfisher -final class BookmarkVC: BaseNavigationViewController { +final class BookmarkVC: BaseNavigationViewController, View { // MARK: - UI components @@ -51,6 +52,7 @@ final class BookmarkVC: BaseNavigationViewController { } private let viewModel = BookmarkVM() + var disposeBag = DisposeBag() // MARK: - Life Cycle @@ -58,15 +60,14 @@ final class BookmarkVC: BaseNavigationViewController { override func viewDidLoad() { super.viewDidLoad() + reactor = viewModel } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) removeCache() - - // TODO: - 서버 에러 해결 후 변경 - viewModel.getBookmarkMock() + viewModel.action.onNext(.entering) } override func configureView() { @@ -81,22 +82,65 @@ final class BookmarkVC: BaseNavigationViewController { configureLayout() } - override func bindRx() { - super.bindRx() - - } - override func bindInput() { super.bindInput() bindBtn() } - override func bindOutput() { - super.bindOutput() + // MARK: - Bind Reactor + + func bind(reactor: BookmarkVM) { + reactor.state.map { $0.isAuthenticated } + .distinctUntilChanged() + .withUnretained(self) + .observe(on: MainScheduler.asyncInstance) + .bind(onNext: { owner, isAuthenticated in + owner.allBookmarkBtn.isHidden = !isAuthenticated + owner.bookmarkTypeCV.isHidden = !isAuthenticated + owner.emptyBookmarkView.isHidden = !isAuthenticated + owner.induceBookmarkView.isHidden = !isAuthenticated + + owner.requestLoginView.isHidden = isAuthenticated + }) + .disposed(by: bag) + + reactor.state.map { $0.bookmarkTotalCount } + .distinctUntilChanged() + .withUnretained(self) + .subscribe(onNext: { owner, count in + owner.allBookmarkBtn.configureButton(for: count > 0 ? .active : .disabled, + count: count) + }) + .disposed(by: disposeBag) + + reactor.state.map { $0.bookmarkWishInfo } + .withUnretained(self) + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { owner, data in + owner.bookmarkTypeCV.reloadData() + }) + .disposed(by: disposeBag) + + reactor.state.map { $0.bookmarkHistoryInfo } + .withUnretained(self) + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { owner, data in + owner.bookmarkTypeCV.reloadData() + }) + .disposed(by: disposeBag) - bindBookmark() - bindType() + reactor.state.map { $0.bookmarkTotalCount } + .map { $0 == 0 } + .distinctUntilChanged() + .withUnretained(self) + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { owner, isEmpty in + owner.bookmarkTypeCV.isHidden = isEmpty + owner.induceBookmarkView.isHidden = isEmpty + owner.emptyBookmarkView.isHidden = !isEmpty + }) + .disposed(by: disposeBag) } @@ -192,6 +236,7 @@ extension BookmarkVC { // MARK: - Input extension BookmarkVC { + private func bindBtn() { // 북마크 모두 보기 allBookmarkBtn.rx.tap @@ -218,74 +263,6 @@ extension BookmarkVC { }) .disposed(by: bag) } -} - - -// MARK: - Output - -extension BookmarkVC { - - private func bindBookmark() { - // 북마크 All 개수 - viewModel.output.BookmarkAllCnt - .withUnretained(self) - .subscribe(onNext: { owner, _ in - let allCnt = owner.viewModel.output.BookmarkAllCnt.value - - owner.allBookmarkBtn.configureButton(for: allCnt > 0 ? .active : .disabled, - count: allCnt) - }) - .disposed(by: bag) - - // 북마크 - 가고싶어요 개수, 이미지 - viewModel.output.BookmarkWishlistInfo - .withUnretained(self) - .subscribe(onNext: { owner, data in - DispatchQueue.main.async { - owner.bookmarkTypeCV.reloadData() - } - }) - .disposed(by: bag) - - // 북마크 - 다녀왔어요 개수, 이미지 - viewModel.output.BookmarkHistoryInfo - .withUnretained(self) - .subscribe(onNext: { owner, data in - DispatchQueue.main.async { - owner.bookmarkTypeCV.reloadData() - } - }) - .disposed(by: bag) - } - - private func bindType() { - // 로그인 여부 체크 - viewModel.output.isAuthenticated - .withUnretained(self) - .bind(onNext: { owner, isAuthenticated in - DispatchQueue.main.async { - owner.allBookmarkBtn.isHidden = !isAuthenticated - owner.bookmarkTypeCV.isHidden = !isAuthenticated - owner.emptyBookmarkView.isHidden = !isAuthenticated - owner.induceBookmarkView.isHidden = !isAuthenticated - - owner.requestLoginView.isHidden = isAuthenticated - } - }) - .disposed(by: bag) - - // 북마크 개수가 0개인지 확인 - viewModel.output.isEmptyBookmark - .withUnretained(self) - .bind(onNext: { owner, isEmptyBookmark in - DispatchQueue.main.async { - owner.bookmarkTypeCV.isHidden = isEmptyBookmark - owner.induceBookmarkView.isHidden = isEmptyBookmark - owner.emptyBookmarkView.isHidden = !isEmptyBookmark - } - }) - .disposed(by: bag) - } } @@ -302,9 +279,9 @@ extension BookmarkVC: UICollectionViewDelegate { var bookmarkSearchType: BookmarkSearchType = .want if indexPath.row == 0 { - guard viewModel.output.BookmarkWishlistInfo.value.cnt != 0 else { return } + guard viewModel.currentState.bookmarkWishInfo.cnt != 0 else { return } } else { - guard viewModel.output.BookmarkHistoryInfo.value.cnt != 0 else { return } + guard viewModel.currentState.bookmarkHistoryInfo.cnt != 0 else { return } bookmarkSearchType = .done } @@ -320,12 +297,13 @@ extension BookmarkVC: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BookmarkTypeCVC.className, for: indexPath) as? BookmarkTypeCVC else { return UICollectionViewCell() } + if indexPath.row == 0 { - cell.configureData(typeInfo: viewModel.output.BookmarkWishlistInfo.value) + cell.configureData(typeInfo: viewModel.currentState.bookmarkWishInfo) } if indexPath.row == 1 { - cell.configureData(typeInfo: viewModel.output.BookmarkHistoryInfo.value) + cell.configureData(typeInfo: viewModel.currentState.bookmarkHistoryInfo) } return cell From dd07db4f9dcd8d7dbf27c4536b58f7f7c534632a Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:25:18 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[CHORE]=20:=20AuthInterceptor=20=EC=8A=A4?= =?UTF-8?q?=EB=A0=88=EB=93=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Reet-Place/Reet-Place/Global/Network/AuthInterceptor.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Reet-Place/Reet-Place/Global/Network/AuthInterceptor.swift b/Reet-Place/Reet-Place/Global/Network/AuthInterceptor.swift index 70f39b9..877a78a 100644 --- a/Reet-Place/Reet-Place/Global/Network/AuthInterceptor.swift +++ b/Reet-Place/Reet-Place/Global/Network/AuthInterceptor.swift @@ -52,7 +52,6 @@ final class AuthInterceptor: RequestInterceptor { completion(.doNotRetryWithError(error)) return } - tabBarVC.showToast(message: "LogoutSuccess".localized, bottomViewHeight: 50.0) myPageVC.updateLoginStatus() print("로그인 만료") From 5fb18f58b806eb62e26e091c01d8e13fac50dbda Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:39:07 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkVM=20ReactorKit?= =?UTF-8?q?=20-=20Reactor=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BookMark/ViewModel/BookmarkMapVM.swift | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkMapVM.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkMapVM.swift index ada4ce8..1b49992 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkMapVM.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkMapVM.swift @@ -5,73 +5,82 @@ // Created by 김태현 on 12/13/23. // -import Foundation import RxSwift import RxCocoa +import ReactorKit -final class BookmarkMapVM { +final class BookmarkMapVM: Reactor { - // MARK: - Variables and Properties + // MARK: - Properties - var input = Input() - var output = Output() - - let network: NetworkProtocol = NetworkProvider() - let apiError = PublishSubject() + enum Action { + case entering(BookmarkSearchType) + } - var bag = DisposeBag() + enum Mutation { + case loadBookmarkSummaries([BookmarkSummaryModel]) + case error + } - struct Input {} - struct Output { - var bookmarkSummaries: BehaviorRelay<[BookmarkSummaryModel]> = BehaviorRelay(value: []) + struct State { + var bookmarkSummaries: [BookmarkSummaryModel] = [] } - // MARK: - Life Cycle + let initialState: State + let network: NetworkProtocol + + + // MARK: - Initializer init() { - bindInput() - bindOutput() + self.initialState = State() + self.network = NetworkProvider() } - deinit { - bag = DisposeBag() + + // MARK: - Mutate, Reduce + + func mutate(action: Action) -> Observable { + switch action { + case .entering(let type): + return getBookmarkSummaries(type: type) + } } + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .loadBookmarkSummaries(let bookmarkSummaries): + newState.bookmarkSummaries = bookmarkSummaries + + case .error: + print("Reactor Error") + } + + return newState + } } -// MARK: - Input - -extension BookmarkMapVM { - func bindInput() {} -} - -// MARK: - Output - -extension BookmarkMapVM { - func bindOutput() {} -} - // MARK: - Networking -extension BookmarkMapVM { +private extension BookmarkMapVM { - func getBookmarkSummaries(type: BookmarkSearchType) { + func getBookmarkSummaries(type: BookmarkSearchType) -> Observable { let path = "/api/bookmarks/all/summaries?searchType=\(type.rawValue)" let endPoint = EndPoint(path: path, httpMethod: .get) - network.request(with: endPoint) - .withUnretained(self) - .subscribe { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { case .success(let summariesReponse): let summaries = summariesReponse.map { $0.toSummary() } - owner.output.bookmarkSummaries.accept(summaries) - case .failure(let error): - owner.apiError.onNext(error) + return .loadBookmarkSummaries(summaries) + case .failure: + return .error } } - .disposed(by: bag) } } From 5f667a4d647c734f3751175d1b84a594e475358a Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 09:39:31 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkVC=20ReactorKit?= =?UTF-8?q?=20-=20View=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reactor 연결 --- .../ViewController/BookmarkMapVC.swift | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkMapVC.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkMapVC.swift index 830dad2..1e43df7 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkMapVC.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkMapVC.swift @@ -9,13 +9,14 @@ import UIKit import RxSwift import RxCocoa +import ReactorKit import Then import SnapKit import NMapsMap -final class BookmarkMapVC: BaseNavigationViewController { +final class BookmarkMapVC: BaseNavigationViewController, View { // MARK: - UI components @@ -26,6 +27,7 @@ final class BookmarkMapVC: BaseNavigationViewController { private let viewModel: BookmarkMapVM = .init() private let bookmarkType: BookmarkSearchType private var markers: [NMFMarker] = .init() + var disposeBag = DisposeBag() // MARK: - Initialize @@ -43,7 +45,8 @@ final class BookmarkMapVC: BaseNavigationViewController { override func viewDidLoad() { super.viewDidLoad() - viewModel.getBookmarkSummaries(type: bookmarkType) + reactor = viewModel + viewModel.action.onNext(.entering(bookmarkType)) } override func configureView() { @@ -57,11 +60,17 @@ final class BookmarkMapVC: BaseNavigationViewController { configureLayout() } + - override func bindOutput() { - super.bindOutput() - - bindSummaries() + // MARK: - Bind Reactor + + func bind(reactor: BookmarkMapVM) { + reactor.state.map { $0.bookmarkSummaries } + .withUnretained(self) + .subscribe { owner, summaries in + owner.configureMarkers(from: summaries) + } + .disposed(by: disposeBag) } // MARK: - Functions @@ -145,18 +154,3 @@ extension BookmarkMapVC { } } - -// MARK: - Bind - -extension BookmarkMapVC { - - private func bindSummaries() { - viewModel.output.bookmarkSummaries - .withUnretained(self) - .subscribe { owner, summaries in - owner.configureMarkers(from: summaries) - } - .disposed(by: bag) - } - -} From c9c52cc6de6679ffbebcb88436793888fbc824b4 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 10:54:28 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkCardListVM=20Rea?= =?UTF-8?q?ctorKit=20-=20Reactor=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/BookmarkCardListVM.swift | 200 +++++++++++------- 1 file changed, 124 insertions(+), 76 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift index 8beb447..58f6214 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift @@ -5,80 +5,129 @@ // Created by 김태현 on 2023/02/18. // -import Foundation import RxSwift import RxCocoa +import ReactorKit -final class BookmarkCardListVM { +final class BookmarkCardListVM: Reactor { - var input = Input() - var output = Output() + // MARK: - Properties - let network: NetworkProtocol = NetworkProvider() - let apiError = PublishSubject() - private let type: BookmarkSearchType - - var bag = DisposeBag() - - struct Input { - var page: BehaviorRelay = BehaviorRelay(value: 0) - let size: Int = 20 + enum Action { + case entering(BookmarkSearchType) + case deleteBookmark(index: Int) + case removeDeletedBookmark(id: Int) + case modifyBookmark(BookmarkInfo) + case nextPage + case togglingInfo(Int) } - struct Output { - var bookmarkList: BehaviorRelay> = BehaviorRelay(value: []) - var isPaging: BehaviorRelay = BehaviorRelay(value: false) - var isLastPage: BehaviorRelay = BehaviorRelay(value: false) + enum Mutation { + case fetchBookmarkList([BookmarkCardModel], Bool) + case modifyBookmarkList([BookmarkCardModel]) + case toggledInfo([BookmarkCardModel]) + case isLoading(Bool) + case error } - init(type: BookmarkSearchType) { - self.type = type + struct State { + var type: BookmarkSearchType + var page: Int = 0 + var size: Int = 20 - bindInput() - bindOutput() + var bookmarkList: [BookmarkCardModel] = [] + var isPaging: Bool = false + var isLastPage: Bool = false } - deinit { - bag = DisposeBag() - } + let initialState: State + let network: NetworkProtocol -} - - -// MARK: - Input - -extension BookmarkCardListVM: Input { - func bindInput() { - + // MARK: - Initializer + + init(type: BookmarkSearchType) { + self.initialState = State(type: type) + self.network = NetworkProvider() } -} - - -// MARK: - Output - -extension BookmarkCardListVM: Output { - func bindOutput() { - + // MARK: - Mutate, Reduce + + func mutate(action: Action) -> Observable { + switch action { + case .entering(let type): + return Observable.concat([ + Observable.just(.isLoading(true)), + getBookmarkList(type: type), + Observable.just(.isLoading(false)) + ]) + + case .deleteBookmark(let index): + return deleteBookmark(index: index) + + case .removeDeletedBookmark(let id): + return deleteBookmark(id: id) + + case .modifyBookmark(let info): + return modifyBookmark(info: info) + + case .nextPage: + return Observable.concat([ + Observable.just(.isLoading(true)), + getBookmarkList(type: currentState.type), + Observable.just(.isLoading(false)) + ]) + + case .togglingInfo(let index): + return toggleBookmarkInfo(index: index) + } } + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .fetchBookmarkList(let bookmarkList, let isLastPage): + newState.page = state.page + 1 + newState.bookmarkList = bookmarkList + newState.isLastPage = isLastPage + + case .modifyBookmarkList(let bookmarkList): + newState.bookmarkList = bookmarkList + + case .toggledInfo(let bookmarkList): + newState.bookmarkList = bookmarkList + + case .isLoading(let isPaging): + newState.isPaging = isPaging + + case .error: + print("Reactor Error") + break + } + + return newState + } } + // MARK: - Functions extension BookmarkCardListVM { - func deleteBookmark(id: Int) { - let originBookmarkList = output.bookmarkList.value + func deleteBookmark(id: Int) -> Observable { + let originBookmarkList = currentState.bookmarkList let deletedBookmarkList = originBookmarkList.filter({ $0.id != id }) - output.bookmarkList.accept(deletedBookmarkList) + + return Observable.just(.modifyBookmarkList(deletedBookmarkList)) } - func modifyBookmark(info: BookmarkInfo) { + func modifyBookmark(info: BookmarkInfo) -> Observable { let card = info.toBookmarkCardModel() - var currentBookmarkList = output.bookmarkList.value + let type = currentState.type + var currentBookmarkList = currentState.bookmarkList + switch type { case .all: currentBookmarkList = currentBookmarkList.map { $0.id == card.id ? card : $0 } @@ -87,10 +136,17 @@ extension BookmarkCardListVM { currentBookmarkList = currentBookmarkList.map { $0.id == card.id ? card : $0 } } else { currentBookmarkList = currentBookmarkList.filter { $0.id != card.id } - } } - output.bookmarkList.accept(currentBookmarkList) + + return Observable.just(.modifyBookmarkList(currentBookmarkList)) + } + + func toggleBookmarkInfo(index: Int) -> Observable { + var bookmarkList = currentState.bookmarkList + bookmarkList[index].infoHidden.toggle() + + return Observable.just(.toggledInfo(bookmarkList)) } } @@ -99,55 +155,47 @@ extension BookmarkCardListVM { extension BookmarkCardListVM { - func getBookmarkList(type: BookmarkSearchType) { - let page = input.page.value + func getBookmarkList(type: BookmarkSearchType) -> Observable { + let page = currentState.page let path = "/api/bookmarks?searchType=\(type.rawValue)&page=\(page)&size=10&sort=LATEST" let endPoint = EndPoint(path: path, httpMethod: .get) - output.isPaging.accept(true) - network.request(with: endPoint) + return network.request(with: endPoint) .withUnretained(self) - .subscribe(onNext: { owner, result in + .map { owner, result -> Mutation in switch result { case .success(let data): let bookmarkList = data.content.map { $0.toBookmarkCardModel() } - let originBookmarkList = owner.output.bookmarkList.value - - owner.output.isLastPage.accept(data.last) - owner.input.page.accept(page + 1) + let originBookmarkList = owner.currentState.bookmarkList + let isLastPage = data.last + if page == 0 { - owner.output.bookmarkList.accept(bookmarkList) + return .fetchBookmarkList(bookmarkList, isLastPage) } else { - owner.output.bookmarkList.accept(originBookmarkList + bookmarkList) + return .fetchBookmarkList(originBookmarkList + bookmarkList, isLastPage) } - case .failure(let error): - owner.apiError.onNext(error) + case .failure: + return .error } - owner.output.isPaging.accept(false) - }) - .disposed(by: bag) + } } - func deleteBookmark(index: Int) { - let id = output.bookmarkList.value[index].id + func deleteBookmark(index: Int) -> Observable { + let id = currentState.bookmarkList[index].id let path = "/api/bookmarks/\(id)" let endPoint = EndPoint(path: path, httpMethod: .delete) - network.request(with: endPoint) + return network.request(with: endPoint) .withUnretained(self) - .subscribe(onNext: { owner, result in + .map { owner, result -> Mutation in switch result { case .success: - let deletedBookmarkList = owner - .output.bookmarkList.value - .filter({ $0.id != id }) - owner.output.bookmarkList.accept(deletedBookmarkList) - case .failure(let error): - print(error) - owner.apiError.onNext(error) + let deletedBookmarkList = owner.currentState.bookmarkList.filter({ $0.id != id }) + return .modifyBookmarkList(deletedBookmarkList) + case .failure: + return .error } - }) - .disposed(by: bag) + } } } From 078a1a31225d0e5d38c87431736d16974ad1ee88 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 10:54:55 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkListVC=20Reactor?= =?UTF-8?q?Kit=20-=20View=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reactor 연결 --- .../ViewController/BookmarkListVC.swift | 64 +++++++++---------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkListVC.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkListVC.swift index 9479904..8a1d843 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkListVC.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkListVC.swift @@ -10,12 +10,13 @@ import UIKit import RxSwift import RxCocoa +import ReactorKit import SnapKit import Then -final class BookmarkListVC: BaseNavigationViewController { - +final class BookmarkListVC: BaseNavigationViewController, View { + // MARK: - UI components override var alias: String { @@ -47,6 +48,7 @@ final class BookmarkListVC: BaseNavigationViewController { private let viewModel: BookmarkCardListVM private let bookmarkType: BookmarkSearchType + var disposeBag: DisposeBag = .init() // MARK: - Initialize @@ -68,13 +70,13 @@ final class BookmarkListVC: BaseNavigationViewController { override func viewDidLoad() { super.viewDidLoad() + reactor = viewModel } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - viewModel.input.page.accept(0) - viewModel.getBookmarkList(type: bookmarkType) + viewModel.action.onNext(.entering(bookmarkType)) } override func configureView() { @@ -95,13 +97,18 @@ final class BookmarkListVC: BaseNavigationViewController { bindButton() } - override func bindOutput() { - super.bindOutput() - - bindBookmarkAll() - } - // MARK: - Functions + // MARK: - Bind Reactor + + func bind(reactor: BookmarkCardListVM) { + reactor.state.map { $0.bookmarkList } + .withUnretained(self) + .observe(on: MainScheduler.asyncInstance) + .subscribe(onNext: { owner, _ in + owner.tableView.reloadData() + }) + .disposed(by: bag) + } } @@ -171,18 +178,6 @@ extension BookmarkListVC { .disposed(by: bag) } - private func bindBookmarkAll() { - viewModel.output.bookmarkList - .subscribe(onNext: { [weak self] _ in - guard let self = self else { return } - - DispatchQueue.main.async { - self.tableView.reloadData() - } - }) - .disposed(by: bag) - } - } @@ -195,8 +190,8 @@ extension BookmarkListVC: UITableViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { if tableView.contentOffset.y > tableView.contentSize.height - tableView.bounds.size.height { - if !viewModel.output.isPaging.value && !viewModel.output.isLastPage.value { - viewModel.getBookmarkList(type: bookmarkType) + if !viewModel.currentState.isPaging && !viewModel.currentState.isLastPage { + viewModel.action.onNext(.nextPage) } } } @@ -207,13 +202,13 @@ extension BookmarkListVC: UITableViewDelegate { extension BookmarkListVC: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - viewModel.output.bookmarkList.value.count + viewModel.currentState.bookmarkList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: BookmarkCardTVC.className, for: indexPath) as? BookmarkCardTVC else { fatalError("No such Cell") } - let bookmarkInfo = viewModel.output.bookmarkList.value[indexPath.row] + let bookmarkInfo = viewModel.currentState.bookmarkList[indexPath.row] cell.configureBookmarkCardTVC(with: bookmarkInfo, bookmarkCardActionDelegate: self, index: indexPath.row) @@ -228,10 +223,9 @@ extension BookmarkListVC: UITableViewDataSource { extension BookmarkListVC: BookmarkCardAction { func infoToggle(index: Int) { - var card = viewModel.output.bookmarkList.value - card[index].infoHidden.toggle() - viewModel.output.bookmarkList.accept(card) - tableView.reloadData() + var cardList = viewModel.currentState.bookmarkList + cardList[index].infoHidden.toggle() + viewModel.action.onNext(.togglingInfo(index)) } func showMenu(index: Int, location: CGRect, selectMenuType: SelectBoxStyle) { @@ -243,13 +237,13 @@ extension BookmarkListVC: BookmarkCardAction { } if row == 1 { - let card = self.viewModel.output.bookmarkList.value[index] + let card = self.viewModel.currentState.bookmarkList[index] UIPasteboard.general.string = card.placeDetailURL self.showToast(message: "LinkCopied".localized) } if row == 2 { - self.viewModel.deleteBookmark(index: index) + self.viewModel.action.onNext(.deleteBookmark(index: index)) } } } @@ -266,20 +260,20 @@ extension BookmarkListVC: BookmarkCardAction { func showBottomSheet(index: Int) { let bottomSheetVC = BookmarkBottomSheetVC(isBookmarking: true) - let cardInfo = viewModel.output.bookmarkList.value[index] + let cardInfo = viewModel.currentState.bookmarkList[index] bottomSheetVC.configureSheetData(with: cardInfo) bottomSheetVC.deletedBookmarkId .withUnretained(self) .subscribe { owner, id in - owner.viewModel.deleteBookmark(id: id) + owner.viewModel.action.onNext(.removeDeletedBookmark(id: id)) } .disposed(by: bag) bottomSheetVC.modifiedBookmarkInfo .withUnretained(self) .subscribe { owner, bookmarkInfo in - owner.viewModel.modifyBookmark(info: bookmarkInfo) + owner.viewModel.action.onNext(.modifyBookmark(bookmarkInfo)) } .disposed(by: bag) From 21f74a61ba278a19d654ab37880a7eeaea13e421 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 22:01:58 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[FEAT]=20:=20BookmarkInfo=20Model=20empty?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/BookmarkListResponseModel.swift | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkListResponseModel.swift b/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkListResponseModel.swift index c6a0b3d..f0f2516 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkListResponseModel.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/Model/BookmarkListResponseModel.swift @@ -47,6 +47,19 @@ struct BookmarkInfo: Decodable { } } +extension BookmarkInfo { + static let empty: Self = .init(id: 0, + member: .empty, + place: .empty, + type: .empty, + thumbnailImage: nil, + rate: 1, + people: nil, + relLink1: nil, + relLink2: nil, + relLink3: nil) +} + struct BookmarkUserInfo: Decodable { let id: Int let uid: String @@ -54,6 +67,10 @@ struct BookmarkUserInfo: Decodable { let nickname: String } +extension BookmarkUserInfo { + static let empty: Self = .init(id: 0, uid: .empty, loginType: .empty, nickname: .empty) +} + struct BookmarkPlaceInfo: Decodable { let id: Int let kakaoPid: String @@ -69,6 +86,21 @@ struct BookmarkPlaceInfo: Decodable { let lng: String } +extension BookmarkPlaceInfo { + static let empty: Self = .init(id: 0, + kakaoPid: .empty, + name: .empty, + url: .empty, + categoryGroupCode: nil, + kakaoCategoryName: .empty, + category: .empty, + subCategory: .empty, + lotNumberAddress: .empty, + roadAddress: .empty, + lat: .empty, + lng: .empty) +} + struct SortInfo: Decodable { let empty: Bool let unsorted: Bool From 08bbe18c84d8aabcb8b2f68e045950db4b2189eb Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 22:02:43 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkBottomSheetVM=20?= =?UTF-8?q?ReactorKit=20-=20Reactor=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/BookmarkBottomSheetVM.swift | 157 ++++++++++-------- 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkBottomSheetVM.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkBottomSheetVM.swift index 76ff286..32085bd 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkBottomSheetVM.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkBottomSheetVM.swift @@ -5,109 +5,131 @@ // Created by 김태현 on 12/3/23. // -import Foundation - import RxCocoa import RxSwift +import ReactorKit -final class BookmarkBottomSheetVM { - - // MARK: - Variables and Properties +final class BookmarkBottomSheetVM: Reactor { - var input = Input() - var output = Output() + // MARK: - Properties - let network: NetworkProtocol = NetworkProvider() - let apiError = PublishSubject() + enum Action { + case deleteBookmark(id: Int) + case modifyBookmark(modifiedInfo: BookmarkCardModel) + case saveBookmark(searchPlaceInfo: SearchPlaceListContent, modifiedInfo: BookmarkCardModel) + } - var bag = DisposeBag() + enum Mutation { + case finishDeletion(Bool) + case finishModification(modifiedInfo: BookmarkInfo) + case finishSave(savedType: BookmarkType) + case error + } - struct Input {} - struct Output { - var isSuccessDelete: PublishSubject = .init() - var isSuccessModify: PublishSubject = .init() - var isSuccessSave: PublishSubject = .init() + struct State { + var isSuccessDelete: Bool = false + var successModifiedInfo: BookmarkInfo = .empty + var savedType: BookmarkType = .standard } - // MARK: - Life Cycle + let initialState: State + let network: NetworkProtocol + + + // MARK: - Initializer init() { - bindInput() - bindOutput() + self.initialState = State() + self.network = NetworkProvider() } - deinit { - bag = DisposeBag() + + // MARK: - Mutate, Reduce + + func mutate(action: Action) -> Observable { + switch action { + case .deleteBookmark(let id): + deleteBookmark(id: id) + + case .modifyBookmark(let modifiedInfo): + modifyBookmark(modifyInfo: modifiedInfo) + + case .saveBookmark(let searchPlaceInfo, let modifiedInfo): + saveBookmark(searchPlaceInfo: searchPlaceInfo, modifiedInfo: modifiedInfo) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .finishDeletion(let isSuccess): + newState.isSuccessDelete = isSuccess + + case .finishModification(let modifiedInfo): + newState.successModifiedInfo = modifiedInfo + + case .finishSave(let savedType): + newState.savedType = savedType + + case .error: + print("Reactor Error") + break + } + + return newState } } -// MARK: - Input - -extension BookmarkBottomSheetVM { - func bindInput() {} -} - -// MARK: - Output - -extension BookmarkBottomSheetVM { - func bindOutput() {} -} - // MARK: - Networking -extension BookmarkBottomSheetVM { +private extension BookmarkBottomSheetVM { - func saveBookmark(searchPlaceInfo: SearchPlaceListContent, modifyInfo: BookmarkCardModel) { + func saveBookmark(searchPlaceInfo: SearchPlaceListContent, modifiedInfo: BookmarkCardModel) -> Observable { let path = "/api/bookmarks" let requestModel = BookmarkSaveRequestModel( place: searchPlaceInfo.toBookmarkSavePlace(), - type: modifyInfo.groupType, - rate: modifyInfo.starCount, - people: modifyInfo.withPeople, - relLink1: modifyInfo.relLink1, - relLink2: modifyInfo.relLink2, - relLink3: modifyInfo.relLink3 + type: modifiedInfo.groupType, + rate: modifiedInfo.starCount, + people: modifiedInfo.withPeople, + relLink1: modifiedInfo.relLink1, + relLink2: modifiedInfo.relLink2, + relLink3: modifiedInfo.relLink3 ) let endPoint = EndPoint(path: path, httpMethod: .post, body: requestModel) - network.request(with: endPoint) - .withUnretained(self) - .subscribe(onNext: { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { case .success(let data): let bookmarkType = BookmarkType(rawValue: data.type) - owner.output.isSuccessSave.onNext(bookmarkType) - case .failure(let error): - print(error) - owner.apiError.onNext(error) + return .finishSave(savedType: bookmarkType) + case .failure: + return .error } - }) - .disposed(by: bag) + } } - func deleteBookmark(id: Int) { + func deleteBookmark(id: Int) -> Observable { let path = "/api/bookmarks/\(id)" let endPoint = EndPoint(path: path, httpMethod: .delete) - network.request(with: endPoint) - .withUnretained(self) - .subscribe(onNext: { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { case .success: - owner.output.isSuccessDelete.onNext(true) - case .failure(let error): - print(error) - owner.apiError.onNext(error) + return .finishDeletion(true) + case .failure: + return .error } - }) - .disposed(by: bag) + } } - func modifyBookmark(modifyInfo: BookmarkCardModel) { + func modifyBookmark(modifyInfo: BookmarkCardModel) -> Observable { let path = "/api/bookmarks/\(modifyInfo.id)" let requestModel = BookmarkModifyRequestModel( type: modifyInfo.groupType, @@ -121,18 +143,15 @@ extension BookmarkBottomSheetVM { httpMethod: .put, body: requestModel) - network.request(with: endPoint) - .withUnretained(self) - .subscribe { owner, result in + return network.request(with: endPoint) + .map { result -> Mutation in switch result { - case .success(let bookmarkInfo): - owner.output.isSuccessModify.onNext(bookmarkInfo) - case .failure(let error): - print(error) - owner.apiError.onNext(error) + case .success(let modifiedInfo): + return .finishModification(modifiedInfo: modifiedInfo) + case .failure: + return .error } } - .disposed(by: bag) } } From 0a89c8efaaa37217623670fcabcbf687f0abaafd Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 22:03:15 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[REFACTOR]=20:=20BookmarkBottomSheetVC=20?= =?UTF-8?q?ReactorKit=20-=20View=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reactor 연결 --- .../BookmarkBottomSheetVC.swift | 86 +++++++++++-------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkBottomSheetVC.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkBottomSheetVC.swift index 2fcfcf0..34ba8ec 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkBottomSheetVC.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewController/BookmarkBottomSheetVC.swift @@ -13,8 +13,9 @@ import Then import RxSwift import RxGesture import RxCocoa +import ReactorKit -final class BookmarkBottomSheetVC: ReetBottomSheet { +final class BookmarkBottomSheetVC: ReetBottomSheet, View { // MARK: - UI components @@ -142,6 +143,7 @@ final class BookmarkBottomSheetVC: ReetBottomSheet { // MARK: - Variables and Properties private let viewModel: BookmarkBottomSheetVM = .init() + var disposeBag: DisposeBag = .init() private var urlField : [ReetTextField] = [] private let isBookmarking: Bool @@ -169,6 +171,12 @@ final class BookmarkBottomSheetVC: ReetBottomSheet { // MARK: - Life Cycle + override func viewDidLoad() { + super.viewDidLoad() + + reactor = viewModel + } + override func configureView() { super.configureView() @@ -184,12 +192,44 @@ final class BookmarkBottomSheetVC: ReetBottomSheet { override func bindRx() { super.bindRx() - bind() bindBtn() bindKeyboard() } + // MARK: - Bind Reactor + + func bind(reactor: BookmarkBottomSheetVM) { + reactor.state.map { $0.isSuccessDelete } + .filter { $0 } + .withUnretained(self) + .subscribe { owner, _ in + guard let id = owner.bookmarkID else { return } + owner.dismissBottomSheet() + owner.deletedBookmarkId.onNext(id) + } + .disposed(by: disposeBag) + + reactor.state.map { $0.successModifiedInfo } + .skip(1) + .withUnretained(self) + .subscribe { owner, modifiedInfo in + owner.dismissBottomSheet() + owner.modifiedBookmarkInfo.onNext(modifiedInfo) + } + .disposed(by: disposeBag) + + reactor.state.map { $0.savedType } + .skip(1) + .withUnretained(self) + .subscribe { owner, type in + owner.dismissBottomSheet() + owner.savedBookmarkType.onNext(type) + } + .disposed(by: disposeBag) + } + + // MARK: - Functions func addUrl() { @@ -209,7 +249,7 @@ final class BookmarkBottomSheetVC: ReetBottomSheet { @objc func deleteBookmark() { guard let id = bookmarkID else { return } - viewModel.deleteBookmark(id: id) + viewModel.action.onNext(.deleteBookmark(id: id)) } func getModifiedBookmarkData() -> BookmarkCardModel? { @@ -407,35 +447,7 @@ extension BookmarkBottomSheetVC { // MARK: - Bind extension BookmarkBottomSheetVC { - - private func bind() { - viewModel.output.isSuccessDelete - .filter { $0 } - .withUnretained(self) - .subscribe { owner, _ in - guard let id = owner.bookmarkID else { return } - owner.dismissBottomSheet() - owner.deletedBookmarkId.onNext(id) - } - .disposed(by: bag) - - viewModel.output.isSuccessModify - .withUnretained(self) - .subscribe { owner, bookmarkInfo in - owner.dismissBottomSheet() - owner.modifiedBookmarkInfo.onNext(bookmarkInfo) - } - .disposed(by: bag) - - viewModel.output.isSuccessSave - .withUnretained(self) - .subscribe { owner, bookmarkType in - owner.dismissBottomSheet() - owner.savedBookmarkType.onNext(bookmarkType) - } - .disposed(by: bag) - } - + private func bindKeyboard() { // 키보드가 올라올 때 keyboardWillShow @@ -471,9 +483,9 @@ extension BookmarkBottomSheetVC { private func bindBtn() { // 관련 url 추가 addBtn.rx.tap - .bind(onNext: { [weak self] _ in - guard let self = self else { return } - self.addUrl() + .withUnretained(self) + .bind(onNext: { owner, _ in + owner.addUrl() }) .disposed(by: bag) @@ -483,7 +495,7 @@ extension BookmarkBottomSheetVC { .throttle(.seconds(1), latest: false, scheduler: MainScheduler.asyncInstance) .bind(onNext: { owner, _ in guard let modifiedData = owner.getModifiedBookmarkData() else { return } - owner.viewModel.modifyBookmark(modifyInfo: modifiedData) + owner.viewModel.action.onNext(.modifyBookmark(modifiedInfo: modifiedData)) }) .disposed(by: bag) @@ -505,7 +517,7 @@ extension BookmarkBottomSheetVC { .bind(onNext: { owner, _ in guard let searchPlaceInfo = owner.searchPlaceInfo, let modifiedData = owner.getModifiedBookmarkData() else { return } - owner.viewModel.saveBookmark(searchPlaceInfo: searchPlaceInfo, modifyInfo: modifiedData) + owner.viewModel.action.onNext(.saveBookmark(searchPlaceInfo: searchPlaceInfo, modifiedInfo: modifiedData)) }) .disposed(by: bag) } From 86a55839566789b08bbac99063ff9f16ed217329 Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 22:04:32 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[CHORE]=20:=20BookmarkCardListVM=20privat?= =?UTF-8?q?e=20=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Screen/BookMark/ViewModel/BookmarkCardListVM.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift index 58f6214..27dd993 100644 --- a/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift +++ b/Reet-Place/Reet-Place/Screen/BookMark/ViewModel/BookmarkCardListVM.swift @@ -114,7 +114,7 @@ final class BookmarkCardListVM: Reactor { // MARK: - Functions -extension BookmarkCardListVM { +private extension BookmarkCardListVM { func deleteBookmark(id: Int) -> Observable { let originBookmarkList = currentState.bookmarkList @@ -153,7 +153,7 @@ extension BookmarkCardListVM { // MARK: - Networking -extension BookmarkCardListVM { +private extension BookmarkCardListVM { func getBookmarkList(type: BookmarkSearchType) -> Observable { let page = currentState.page From 384a13fd1255512f69537361d9c4f471b3aeb19f Mon Sep 17 00:00:00 2001 From: kth1210 Date: Wed, 27 Mar 2024 22:08:35 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[CHORE]=20:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20=EB=B3=80=EA=B2=BD=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Reet-Place/Reet-Place.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Reet-Place/Reet-Place.xcodeproj/project.pbxproj b/Reet-Place/Reet-Place.xcodeproj/project.pbxproj index cb6e1ad..cf11ba9 100644 --- a/Reet-Place/Reet-Place.xcodeproj/project.pbxproj +++ b/Reet-Place/Reet-Place.xcodeproj/project.pbxproj @@ -16,7 +16,7 @@ 0E1B62CF2B315AD000C1E609 /* BookmarkSummaryResponseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B62CE2B315AD000C1E609 /* BookmarkSummaryResponseModel.swift */; }; 0E1B62D12B3174FE00C1E609 /* DeleteAccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B62D02B3174FE00C1E609 /* DeleteAccountModel.swift */; }; 0E1B62D32B332D2C00C1E609 /* BookmarkSaveRequestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E1B62D22B332D2C00C1E609 /* BookmarkSaveRequestModel.swift */; }; - 0E2D6F2A29EBBB7D0053CD90 /* BookmarkMainModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D6F2929EBBB7D0053CD90 /* BookmarkMainModel.swift */; }; + 0E2D6F2A29EBBB7D0053CD90 /* BookmarkTypeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D6F2929EBBB7D0053CD90 /* BookmarkTypeInfo.swift */; }; 0E2D6F2C29EBBD180053CD90 /* UIImageView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D6F2B29EBBD180053CD90 /* UIImageView+.swift */; }; 0E2D6F5429F140A80053CD90 /* ReetPopUp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E2D6F5329F140A80053CD90 /* ReetPopUp.swift */; }; 0E4971E02BA463CF0006BDB1 /* EndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4971DF2BA463CF0006BDB1 /* EndPoint.swift */; }; @@ -226,7 +226,7 @@ 0E1B62CE2B315AD000C1E609 /* BookmarkSummaryResponseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkSummaryResponseModel.swift; sourceTree = ""; }; 0E1B62D02B3174FE00C1E609 /* DeleteAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAccountModel.swift; sourceTree = ""; }; 0E1B62D22B332D2C00C1E609 /* BookmarkSaveRequestModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkSaveRequestModel.swift; sourceTree = ""; }; - 0E2D6F2929EBBB7D0053CD90 /* BookmarkMainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkMainModel.swift; sourceTree = ""; }; + 0E2D6F2929EBBB7D0053CD90 /* BookmarkTypeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkTypeInfo.swift; sourceTree = ""; }; 0E2D6F2B29EBBD180053CD90 /* UIImageView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+.swift"; sourceTree = ""; }; 0E2D6F5329F140A80053CD90 /* ReetPopUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReetPopUp.swift; sourceTree = ""; }; 0E4971DF2BA463CF0006BDB1 /* EndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndPoint.swift; sourceTree = ""; }; @@ -507,7 +507,7 @@ 0EA432DE29A10F0900C4D47F /* Model */ = { isa = PBXGroup; children = ( - 0E2D6F2929EBBB7D0053CD90 /* BookmarkMainModel.swift */, + 0E2D6F2929EBBB7D0053CD90 /* BookmarkTypeInfo.swift */, 0EA432DF29A10F2300C4D47F /* BookmarkCardModel.swift */, 0E502B782A919DEE002C71F2 /* BookmarkCountResponseModel.swift */, 0E502B722A9197BB002C71F2 /* BookmarkListResponseModel.swift */, @@ -1421,7 +1421,7 @@ 3C3255E32A35ED1300F96F82 /* SearchVC.swift in Sources */, 3C72D92C2B3401F200AF2190 /* ReetPlace.xcdatamodeld in Sources */, 3C4DA53F2A56518900274692 /* LoginVC.swift in Sources */, - 0E2D6F2A29EBBB7D0053CD90 /* BookmarkMainModel.swift in Sources */, + 0E2D6F2A29EBBB7D0053CD90 /* BookmarkTypeInfo.swift in Sources */, 3C2784162A0A652E0095B706 /* ReetMenuTarBarCVC.swift.swift in Sources */, 0E1B62CD2B315AB900C1E609 /* BookmarkSummaryModel.swift in Sources */, A4AABE6F299BC0D400BA9D37 /* AssetFonts.swift in Sources */,