From 72329d6f60df97e66dff3fe1995f665b6ddecbe9 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Sat, 15 Nov 2025 02:13:12 +0900 Subject: [PATCH] =?UTF-8?q?feat(series-all-home):=20=EC=8B=9C=EB=A6=AC?= =?UTF-8?q?=EC=A6=88=20=EC=A0=84=EC=B2=B4=EB=B3=B4=EA=B8=B0=20=ED=99=88=20?= =?UTF-8?q?=ED=83=AD=20API=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Main/Home/SeriesMainHomeResponse.swift | 19 +++++++ .../Series/Main/Home/SeriesMainHomeView.swift | 12 ++++- .../Main/Home/SeriesMainHomeViewModel.swift | 49 +++++++++++++++++++ .../Content/Series/Main/SeriesMainApi.swift | 40 +++++++++++++++ .../Series/Main/SeriesMainRepository.swift | 16 ++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeResponse.swift diff --git a/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeResponse.swift b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeResponse.swift new file mode 100644 index 0000000..5298848 --- /dev/null +++ b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeResponse.swift @@ -0,0 +1,19 @@ +// +// SeriesMainHomeResponse.swift +// SodaLive +// +// Created by klaus on 11/15/25. +// + +import Foundation + +struct SeriesMainHomeResponse: Decodable { + let banners: [SeriesBannerResponse] + let completedSeriesList: [SeriesListItem] + let recommendSeriesList: [SeriesListItem] +} + +struct SeriesBannerResponse: Decodable { + let seriesId: Int + let imagePath: String +} diff --git a/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swift b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swift index f37776d..69997c1 100644 --- a/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swift +++ b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swift @@ -8,8 +8,18 @@ import SwiftUI struct SeriesMainHomeView: View { + + @StateObject var viewModel = SeriesMainHomeViewModel() + var body: some View { - Text("시리즈 전체보기 홈") + ScrollView(.vertical, showsIndicators: false) { + VStack(spacing: 48) { + + } + } + .onAppear { + viewModel.fetchHome() + } } } diff --git a/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeViewModel.swift b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeViewModel.swift index 9ca70ea..63a880a 100644 --- a/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeViewModel.swift +++ b/SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeViewModel.swift @@ -6,3 +6,52 @@ // import Foundation +import Combine + +final class SeriesMainHomeViewModel: ObservableObject { + private let repository = SeriesMainRepository() + private var subscription = Set() + + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + + func fetchHome() { + isLoading = true + repository + .fetchHome() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let data = decoded.data, decoded.success { + DEBUG_LOG("data: \(data)") + } else { + if let message = decoded.message { + self.errorMessage = message + } else { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + } + + self.isShowPopup = true + } + } catch { + self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." + self.isShowPopup = true + } + + self.isLoading = false + } + .store(in: &subscription) + } +} diff --git a/SodaLive/Sources/Content/Series/Main/SeriesMainApi.swift b/SodaLive/Sources/Content/Series/Main/SeriesMainApi.swift index cbcf8e0..d588b4f 100644 --- a/SodaLive/Sources/Content/Series/Main/SeriesMainApi.swift +++ b/SodaLive/Sources/Content/Series/Main/SeriesMainApi.swift @@ -6,3 +6,43 @@ // import Foundation +import Moya + +enum SeriesMainApi { + case fetchHome(isAdultContentVisible: Bool, contentType: ContentType) +} + +extension SeriesMainApi: TargetType { + // MARK: API 별로 바뀌는 부분 + var path: String { + switch self { + case .fetchHome: + return "/audio-content/series/main" + } + } + + var task: Moya.Task { + switch self { + case .fetchHome(let isAdultContentVisible, let contentType): + let parameters = [ + "isAdultContentVisible": isAdultContentVisible, + "contentType": contentType, + ] as [String : Any] + + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + } + } + + // MARK: 공통적인 부분 + var baseURL: URL { + return URL(string: BASE_URL)! + } + + var method: Moya.Method { + return .get + } + + var headers: [String : String]? { + return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] + } +} diff --git a/SodaLive/Sources/Content/Series/Main/SeriesMainRepository.swift b/SodaLive/Sources/Content/Series/Main/SeriesMainRepository.swift index 283e155..073a7da 100644 --- a/SodaLive/Sources/Content/Series/Main/SeriesMainRepository.swift +++ b/SodaLive/Sources/Content/Series/Main/SeriesMainRepository.swift @@ -6,3 +6,19 @@ // import Foundation +import CombineMoya +import Combine +import Moya + +class SeriesMainRepository { + private let api = MoyaProvider() + + func fetchHome() -> AnyPublisher { + return api.requestPublisher( + .fetchHome( + isAdultContentVisible: UserDefaults.isAdultContentVisible(), + contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL + ) + ) + } +}