diff --git a/challenge/challenge/Model/HomeSection.swift b/challenge/challenge/Model/HomeSection.swift index af686ce..3e9beb8 100644 --- a/challenge/challenge/Model/HomeSection.swift +++ b/challenge/challenge/Model/HomeSection.swift @@ -55,3 +55,8 @@ enum LayoutType { case card case list } + +struct MusicSection { + let section: HomeSection + let items: [Music] +} diff --git a/challenge/challenge/Model/Music.swift b/challenge/challenge/Model/Music.swift deleted file mode 100644 index 21a08c4..0000000 --- a/challenge/challenge/Model/Music.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// Music.swift -// challenge -// -// Created by 손영빈 on 3/12/26. -// - -import Foundation - -struct MusicResponse: Codable { - let results: [Music] -} - -struct Music: Codable, Hashable { - let trackName: String? - let artistName: String? - let collectionName: String? - let artworkUrl60: String? // 앨범커버 - let artworkUrl100: String? -} - -struct MusicSection { - let section: HomeSection - let items: [Music] -} diff --git a/challenge/challenge/Model/Podcast.swift b/challenge/challenge/Model/Podcast.swift deleted file mode 100644 index aff4230..0000000 --- a/challenge/challenge/Model/Podcast.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Podcast.swift -// challenge -// -// Created by 손영빈 on 3/17/26. -// - -import Foundation - -struct PodcastResponse: Codable { - let results: [Podcast] -} - -struct Podcast: Codable, Hashable { - let trackName: String? - let artistName: String? - let artworkUrl100: String? - let artworkUrl600: String? -} diff --git a/challenge/challenge/Model/iTunesResponse.swift b/challenge/challenge/Model/iTunesResponse.swift new file mode 100644 index 0000000..22dc20a --- /dev/null +++ b/challenge/challenge/Model/iTunesResponse.swift @@ -0,0 +1,27 @@ +// +// iTunesResponse.swift +// challenge +// +// Created by 손영빈 on 3/19/26. +// + +import Foundation + +struct iTunesResponse: Codable { + let results: [T] +} + +struct Music: Codable, Hashable { + let trackName: String? + let artistName: String? + let collectionName: String? + let artworkUrl60: String? // 앨범커버 + let artworkUrl100: String? +} + +struct Podcast: Codable, Hashable { + let trackName: String? + let artistName: String? + let artworkUrl100: String? + let artworkUrl600: String? +} diff --git a/challenge/challenge/ViewModel/HomeViewModel.swift b/challenge/challenge/ViewModel/HomeViewModel.swift index 8d22dd0..59ab689 100644 --- a/challenge/challenge/ViewModel/HomeViewModel.swift +++ b/challenge/challenge/ViewModel/HomeViewModel.swift @@ -42,7 +42,7 @@ class HomeViewModel: ViewModel { let observable = sectionInfo.map { section, term -> Observable in guard let url = NetworkManager.shared.url(term: term, media: "music") else { return .error(NetworkError.requestError) } return NetworkManager.shared.fetch(url: url) - .map { (response: MusicResponse) in // MusicResponse -> MusicSection으로 반환 + .map { (response: iTunesResponse) in // MusicResponse -> MusicSection으로 반환 MusicSection(section: section, items: response.results) }.asObservable() } diff --git a/challenge/challenge/ViewModel/SearchViewModel.swift b/challenge/challenge/ViewModel/SearchViewModel.swift index b7d7b78..29750f0 100644 --- a/challenge/challenge/ViewModel/SearchViewModel.swift +++ b/challenge/challenge/ViewModel/SearchViewModel.swift @@ -28,38 +28,27 @@ class SearchViewModel: ViewModel { .filter { !$0.isEmpty } .share() - let podcast = searchText - .flatMap { [weak self] term -> Observable<[Podcast]> in - guard let self else { return .just([] as [Podcast])} - guard let url = NetworkManager.shared.url(term: term, media: "podcast") else { - self.errorSubject.onNext(.requestError) - return .just([] as [Podcast]) - } - return NetworkManager.shared.fetch(url: url) - .map { (response: PodcastResponse) in response.results } - .asObservable() - .catch { [weak self] error in - self?.errorSubject.onNext(error as! NetworkError) - return .just([] as [Podcast]) - } - } - - let music = searchText - .flatMap { [weak self] term -> Observable<[Music]> in - guard let self else { return .just([] as [Music]) } - guard let url = NetworkManager.shared.url(term: term, media: "music") else { - self.errorSubject.onNext(.requestError) - return .just([] as [Music])} - return NetworkManager.shared.fetch(url: url) - .map { (response: MusicResponse) in response.results } - .asObservable() - .catch { [weak self] error in - self?.errorSubject.onNext(error as! NetworkError) - return .just([] as [Music]) - } - } - let searchResult = Observable.zip(podcast, music) + let podcast: Observable<[Podcast]> = search(searchText: searchText, media: "podcast") + let music: Observable<[Music]> = search(searchText: searchText, media: "music") + let searchResult = Observable.zip(podcast, music).share() return Output(searchResult: searchResult, error: errorSubject.asObservable()) } + + private func search(searchText: Observable, media: String) -> Observable<[T]> { + searchText.flatMap { [weak self] term -> Observable<[T]> in + guard let self else { return .just([])} + guard let url = NetworkManager.shared.url(term: term, media: media) else { + self.errorSubject.onNext(.requestError) + return .just([]) + } + return NetworkManager.shared.fetch(url: url) + .map { (response: iTunesResponse) in response.results } + .asObservable() + .catch { [weak self] error in + self?.errorSubject.onNext(error as! NetworkError) + return .just([]) + } + } + } }