From 37e65f33cb13efde3ca2b4b17ee55f735dd10ac7 Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:14:18 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20NavigationStore=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/BoxOffice/BoxOfficeApp.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BoxOffice/BoxOffice/BoxOfficeApp.swift b/BoxOffice/BoxOffice/BoxOfficeApp.swift index 0eb1098..c27d24a 100644 --- a/BoxOffice/BoxOffice/BoxOfficeApp.swift +++ b/BoxOffice/BoxOffice/BoxOfficeApp.swift @@ -9,9 +9,12 @@ import SwiftUI @main struct BoxOfficeApp: App { + @StateObject private var navigationStore = NavigationStore() + var body: some Scene { WindowGroup { - BoxOfficeView(viewModel: BoxOfficeViewModel()) + BoxOfficeView(store: BoxOfficeStore()) + .environmentObject(navigationStore) } } } From f37cf41830c7035bc5e46e2b41053141b05bb129 Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:14:46 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20BoxOfficeServiceProtocol=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOffice/Network/Service/BoxOfficeService.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/BoxOffice/BoxOffice/Network/Service/BoxOfficeService.swift b/BoxOffice/BoxOffice/Network/Service/BoxOfficeService.swift index 0a38a66..263e600 100644 --- a/BoxOffice/BoxOffice/Network/Service/BoxOfficeService.swift +++ b/BoxOffice/BoxOffice/Network/Service/BoxOfficeService.swift @@ -9,8 +9,12 @@ import Foundation import Moya -final class BoxOfficeService { - static let shared = BoxOfficeService() +protocol BoxOfficeServiceProtocol { + func fetchBoxOffice(date: String) async throws -> BoxOfficeResponse + func fetchMovieDetail(movieId: String) async throws -> MovieResponse +} + +final class BoxOfficeService: BoxOfficeServiceProtocol { private let provider = MoyaProvider(plugins: [MoyaLoggingPlugin()]) From 79b4caf30c421bc0cc8456e70fba0cc5b8dbc420 Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:15:11 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20BoxOfficeDomain=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/BoxOffice/BoxOfficeDomain.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeDomain.swift diff --git a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeDomain.swift b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeDomain.swift new file mode 100644 index 0000000..d614341 --- /dev/null +++ b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeDomain.swift @@ -0,0 +1,22 @@ +// +// BoxOfficeDomain.swift +// BoxOffice +// +// Created by 최안용 on 1/8/25. +// + +import Foundation + +struct BoxOfficeState { + var boxOffice: [DetailBoxOffice] = [] + var movie: MovieInfo? = nil + var date: String = "" +} + + +enum BoxOfficeIntent { + case onAppear + case changeDate(plus: Int) + case fetchBoxOffice + case fetchMovieInfo(movieId: String) +} From 498e85d5642c9f39fae71a90615dc2ca94e0000a Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:15:37 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20BoxOfficeStore=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/BoxOffice/BoxOfficeStore.swift | 94 +++++++++++++++++++ .../BoxOffice/BoxOfficeViewModel.swift | 54 ----------- 2 files changed, 94 insertions(+), 54 deletions(-) create mode 100644 BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeStore.swift delete mode 100644 BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeViewModel.swift diff --git a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeStore.swift b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeStore.swift new file mode 100644 index 0000000..9e369fc --- /dev/null +++ b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeStore.swift @@ -0,0 +1,94 @@ +// +// BoxOfficeStore.swift +// BoxOffice +// +// Created by 최안용 on 1/5/25. +// + +import Foundation + +final class BoxOfficeStore: ObservableObject { + @Published private(set) var state: BoxOfficeState + + private let boxOfficeService: BoxOfficeService + + init( + state: BoxOfficeState = BoxOfficeState(), + boxOfficeService: BoxOfficeService = BoxOfficeService() + ) { + self.state = state + self.boxOfficeService = boxOfficeService + } + + func dispatch(_ intent: BoxOfficeIntent) { + switch intent { + case .onAppear: + Task { + do { + await currentDate() + try await fetchBoxOffice() + } catch { + print(error) + } + } + case .changeDate(let plus): + Task { + do { + await calulateDate(plus) + try await fetchBoxOffice() + } catch { + print(error) + } + } + case .fetchBoxOffice: + Task { + do { + try await fetchBoxOffice() + } catch { + print(error) + } + } + case .fetchMovieInfo(let movieId): + Task { + do { + try await fetchMovieInfo(movieId) + } catch { + print(error) + } + } + } + } +} + +//MARK: - Functions + +extension BoxOfficeStore { + @MainActor + private func currentDate() { + let date = Date() + let df = DateFormatter() + df.dateFormat = "yyyy-MM-dd" + state.date = df.string(from: date) + } + + @MainActor + private func fetchBoxOffice() async throws { + let date: String = state.date.split(separator: "-").map { String($0) }.joined() + state.boxOffice = try await boxOfficeService.fetchBoxOffice(date: date).boxOfficeResult.dailyBoxOfficeList + } + + @MainActor + private func calulateDate(_ plus: Int) { + let df = DateFormatter() + df.dateFormat = "yyyy-MM-dd" + guard let currentDate = df.date(from: state.date) else { return } + + let resultDate = Calendar.current.date(byAdding: .day, value: plus, to: currentDate) + state.date = df.string(from: resultDate ?? Date()) + } + + @MainActor + private func fetchMovieInfo(_ movieId: String) async throws { + state.movie = try await boxOfficeService.fetchMovieDetail(movieId: movieId).movieInfoResult.movieInfo + } +} diff --git a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeViewModel.swift b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeViewModel.swift deleted file mode 100644 index 18abf64..0000000 --- a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeViewModel.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// BoxOfficeViewModel.swift -// BoxOffice -// -// Created by 최안용 on 1/5/25. -// - -import Foundation - -@MainActor -final class BoxOfficeViewModel: ObservableObject { - @Published var boxOffice: [DetailBoxOffice] = [] - @Published var movie: MovieInfo? - @Published var date: String = "" { - didSet { - Task { - try await fetchBoxOffice() - } - } - } - - init() { - currentDate() - } -} - -//MARK: - Functions - -extension BoxOfficeViewModel { - private func currentDate() { - let date = Date() - let df = DateFormatter() - df.dateFormat = "yyyy-MM-dd" - self.date = df.string(from: date) - } - - private func fetchBoxOffice() async throws { - let date: String = date.split(separator: "-").map { String($0) }.joined() - self.boxOffice = try await BoxOfficeService.shared.fetchBoxOffice(date: date).boxOfficeResult.dailyBoxOfficeList - } - - func calulateDate(_ plus: Int) { - let df = DateFormatter() - df.dateFormat = "yyyy-MM-dd" - guard let currentDate = df.date(from: date) else { return } - - let resultDate = Calendar.current.date(byAdding: .day, value: plus, to: currentDate) - self.date = df.string(from: resultDate ?? Date()) - } - - func fetchMovieInfo(_ movieId: String) async throws { - self.movie = try await BoxOfficeService.shared.fetchMovieDetail(movieId: movieId).movieInfoResult.movieInfo - } -} From 84fa1cb7e6b822c7ed85446cf6b8a6e046c61ada Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:15:53 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20BoxOfficeView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/BoxOffice/BoxOfficeView.swift | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeView.swift b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeView.swift index 1d446b9..cdd62d9 100644 --- a/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeView.swift +++ b/BoxOffice/BoxOffice/Source/Features/BoxOffice/BoxOfficeView.swift @@ -8,28 +8,33 @@ import SwiftUI struct BoxOfficeView: View { - @StateObject var viewModel: BoxOfficeViewModel + @StateObject var store: BoxOfficeStore + @EnvironmentObject private var navigationStore: NavigationStore var body: some View { - NavigationStack { + NavigationStack(path: $navigationStore.path) { ScrollView(showsIndicators: false) { - TitleView(viewModel: viewModel) - BoxOfficeListView(viewModel: viewModel) + TitleView(store: store) + BoxOfficeListView(store: store) } - .navigationDestination(item: $viewModel.movie) { movie in - MovieDetailView(movie: movie) + .navigationDestination(for: ViewType.self) { view in + navigationStore.bulid(view) + .environmentObject(store) } } + .onAppear { + store.dispatch(.onAppear) + } } } //MARK: - TitleView private struct TitleView: View { - @ObservedObject private var viewModel: BoxOfficeViewModel + @ObservedObject private var store: BoxOfficeStore - fileprivate init(viewModel: BoxOfficeViewModel) { - self.viewModel = viewModel + fileprivate init(store: BoxOfficeStore) { + self.store = store } fileprivate var body: some View { @@ -40,17 +45,17 @@ private struct TitleView: View { HStack { Button { - viewModel.calulateDate(-1) + store.dispatch(.changeDate(plus: -1)) } label: { Image(systemName: "control") .rotationEffect(Angle(degrees: -90)) } - Text("\(viewModel.date)") + Text("\(store.state.date)") .font(.headline) Button { - viewModel.calulateDate(1) + store.dispatch(.changeDate(plus: 1)) } label: { Image(systemName: "control") .rotationEffect(Angle(degrees: 90)) @@ -64,25 +69,24 @@ private struct TitleView: View { //MARK: - BoxOfficeListView private struct BoxOfficeListView: View { - @ObservedObject private var viewModel: BoxOfficeViewModel + @ObservedObject private var store: BoxOfficeStore + @EnvironmentObject private var navigationStore: NavigationStore - fileprivate init(viewModel: BoxOfficeViewModel) { - self.viewModel = viewModel + fileprivate init(store: BoxOfficeStore) { + self.store = store } fileprivate var body: some View { Group { - if viewModel.boxOffice.isEmpty { + if store.state.boxOffice.isEmpty { Text("집계되지 않은 날짜입니다!") .font(.headline) .padding(.top, 300) } else { - ForEach(viewModel.boxOffice, id: \.self) { boxOffice in + ForEach(store.state.boxOffice, id: \.self) { boxOffice in BoxOfficeCell(boxOffice: boxOffice) .onTapGesture { - Task { - try await viewModel.fetchMovieInfo(boxOffice.movieCd) - } + navigationStore.dispatch(.push(.movieDetail(boxOffice.movieCd))) } } } @@ -131,5 +135,5 @@ private struct BoxOfficeCell:View { } #Preview { - BoxOfficeView(viewModel: BoxOfficeViewModel()) + BoxOfficeView(store: BoxOfficeStore()) } From 539f7d42ecc15a36a564135d849c5bc041c1a8d5 Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:16:06 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20Navigation=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Common/NavigationDomain.swift | 17 +++++++++++ .../Source/Common/NavigationStore.swift | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 BoxOffice/BoxOffice/Source/Common/NavigationDomain.swift create mode 100644 BoxOffice/BoxOffice/Source/Common/NavigationStore.swift diff --git a/BoxOffice/BoxOffice/Source/Common/NavigationDomain.swift b/BoxOffice/BoxOffice/Source/Common/NavigationDomain.swift new file mode 100644 index 0000000..82da633 --- /dev/null +++ b/BoxOffice/BoxOffice/Source/Common/NavigationDomain.swift @@ -0,0 +1,17 @@ +// +// NavigationDomain.swift +// BoxOffice +// +// Created by 최안용 on 1/12/25. +// + +import Foundation + +enum NavigationIntent { + case push(ViewType) + case pop +} + +enum ViewType: Hashable { + case movieDetail(String) +} diff --git a/BoxOffice/BoxOffice/Source/Common/NavigationStore.swift b/BoxOffice/BoxOffice/Source/Common/NavigationStore.swift new file mode 100644 index 0000000..7f0669a --- /dev/null +++ b/BoxOffice/BoxOffice/Source/Common/NavigationStore.swift @@ -0,0 +1,29 @@ +// +// NavigationStore.swift +// BoxOffice +// +// Created by 최안용 on 1/12/25. +// + +import SwiftUI + +final class NavigationStore: ObservableObject { + @Published var path: [ViewType] = [] + + @ViewBuilder + func bulid(_ view: ViewType) -> some View { + switch view { + case .movieDetail(let movieId): + MovieDetailView(movieId: movieId) + } + } + + func dispatch(_ intent: NavigationIntent) { + switch intent { + case .push(let view): + path.append(view) + case .pop: + path.removeLast() + } + } +} From c00f87c4c4c90edea759a036724ade60f292e10b Mon Sep 17 00:00:00 2001 From: ChoiAnYong Date: Sun, 12 Jan 2025 14:16:19 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20MovieDetailView=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Movie/MovieDetailView.swift | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/BoxOffice/BoxOffice/Source/Features/Movie/MovieDetailView.swift b/BoxOffice/BoxOffice/Source/Features/Movie/MovieDetailView.swift index 45216e9..9a3b970 100644 --- a/BoxOffice/BoxOffice/Source/Features/Movie/MovieDetailView.swift +++ b/BoxOffice/BoxOffice/Source/Features/Movie/MovieDetailView.swift @@ -8,20 +8,24 @@ import SwiftUI struct MovieDetailView: View { - let movie: MovieInfo + @EnvironmentObject private var store: BoxOfficeStore + let movieId: String var body: some View { VStack(alignment: .leading, spacing: 10) { - Text("\(movie.movieNm)") - Text("상영 시간: \(movie.showTm)") - Text("개봉일: \(movie.openDt)") - Text("영화 유형: \(movie.typeNm)") - Text("제작 국가: \(movie.nations.first?.nationNm ?? "오류")") - Text("장르: \(movie.genres.first?.genreNm ?? "오류")") + Text("\(store.state.movie?.movieNm ?? "")") + Text("상영 시간: \(store.state.movie?.showTm ?? "")") + Text("개봉일: \(store.state.movie?.openDt ?? "")") + Text("영화 유형: \(store.state.movie?.typeNm ?? "")") + Text("제작 국가: \(store.state.movie?.nations.first?.nationNm ?? "오류")") + Text("장르: \(store.state.movie?.genres.first?.genreNm ?? "오류")") + } + .onAppear { + store.dispatch(.fetchMovieInfo(movieId: movieId)) } } } #Preview { - MovieDetailView(movie: .init(movieNm: "dk", showTm: "dlkjf", openDt: "djfowing", typeNm: "dklsjf", nations: [], genres: [])) + MovieDetailView(movieId: "kd") }