diff --git a/Projects/App/Sources/Application/AppComponent+Credit.swift b/Projects/App/Sources/Application/AppComponent+Credit.swift index 84a4a88f6..4290e0a91 100644 --- a/Projects/App/Sources/Application/AppComponent+Credit.swift +++ b/Projects/App/Sources/Application/AppComponent+Credit.swift @@ -24,6 +24,12 @@ public extension AppComponent { } } + var fetchCreditProfileImageURLUseCase: any FetchCreditProfileImageURLUseCase { + shared { + FetchCreditProfileImageURLUseCaseImpl(creditRepository: creditRepository) + } + } + var songCreditFactory: any SongCreditFactory { SongCreditComponent(parent: self) } diff --git a/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift b/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift index 1cc4162ed..9642713ac 100644 --- a/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift +++ b/Projects/Domains/CreditDomain/Interface/DataSource/RemoteCreditDataSource.swift @@ -9,4 +9,6 @@ public protocol RemoteCreditDataSource { page: Int, limit: Int ) -> Single<[SongEntity]> + + func fetchCreditProfileImageURL(name: String) -> Single } diff --git a/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift b/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift index 920465675..3dd548689 100644 --- a/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift +++ b/Projects/Domains/CreditDomain/Interface/Repository/CreditRepository.swift @@ -9,4 +9,6 @@ public protocol CreditRepository { page: Int, limit: Int ) -> Single<[SongEntity]> + + func fetchCreditProfileImageURL(name: String) -> Single } diff --git a/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileImageURLUseCase.swift b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileImageURLUseCase.swift new file mode 100644 index 000000000..1fa8cc947 --- /dev/null +++ b/Projects/Domains/CreditDomain/Interface/UseCase/FetchCreditProfileImageURLUseCase.swift @@ -0,0 +1,5 @@ +import RxSwift + +public protocol FetchCreditProfileImageURLUseCase { + func execute(name: String) -> Single +} diff --git a/Projects/Domains/CreditDomain/Sources/API/CreditAPI.swift b/Projects/Domains/CreditDomain/Sources/API/CreditAPI.swift index 4a503e7fb..088d04da3 100644 --- a/Projects/Domains/CreditDomain/Sources/API/CreditAPI.swift +++ b/Projects/Domains/CreditDomain/Sources/API/CreditAPI.swift @@ -12,6 +12,7 @@ public enum CreditAPI { page: Int, limit: Int ) + case fetchProfileImageURL(name: String) } extension CreditAPI: WMAPI { @@ -23,12 +24,14 @@ extension CreditAPI: WMAPI { switch self { case let .fetchCreditSongList(name, _, _, _): return "/\(name)/songs" + case let .fetchProfileImageURL(name): + return "/\(name)" } } public var method: Moya.Method { switch self { - case .fetchCreditSongList: + case .fetchCreditSongList, .fetchProfileImageURL: return .get } } @@ -41,12 +44,15 @@ extension CreditAPI: WMAPI { "page": page, "limit": limit ], encoding: URLEncoding.queryString) + + case .fetchProfileImageURL: + return .requestPlain } } public var jwtTokenType: JwtTokenType { switch self { - case .fetchCreditSongList: + case .fetchCreditSongList, .fetchProfileImageURL: return .none } } diff --git a/Projects/Domains/CreditDomain/Sources/DataSource/RemoteCreditDataSourceImpl.swift b/Projects/Domains/CreditDomain/Sources/DataSource/RemoteCreditDataSourceImpl.swift index 087dae52d..39d2fb11f 100644 --- a/Projects/Domains/CreditDomain/Sources/DataSource/RemoteCreditDataSourceImpl.swift +++ b/Projects/Domains/CreditDomain/Sources/DataSource/RemoteCreditDataSourceImpl.swift @@ -15,4 +15,10 @@ public final class RemoteCreditDataSourceImpl: BaseRemoteDataSource, .map([FetchCreditSongListResponseDTO].self) .map { $0.toDomain() } } + + public func fetchCreditProfileImageURL(name: String) -> Single { + request(.fetchProfileImageURL(name: name)) + .map(FetchCreditProfileImageURLResponseDTO.self) + .map(\.profileURL) + } } diff --git a/Projects/Domains/CreditDomain/Sources/Repository/CreditRepositoryImpl.swift b/Projects/Domains/CreditDomain/Sources/Repository/CreditRepositoryImpl.swift index 0a6800ed7..566db2e66 100644 --- a/Projects/Domains/CreditDomain/Sources/Repository/CreditRepositoryImpl.swift +++ b/Projects/Domains/CreditDomain/Sources/Repository/CreditRepositoryImpl.swift @@ -20,4 +20,8 @@ public final class CreditRepositoryImpl: CreditRepository { ) -> Single<[SongEntity]> { remoteCreditDataSource.fetchCreditSongList(name: name, order: order, page: page, limit: limit) } + + public func fetchCreditProfileImageURL(name: String) -> Single { + remoteCreditDataSource.fetchCreditProfileImageURL(name: name) + } } diff --git a/Projects/Domains/CreditDomain/Sources/ResponseDTO/FetchCreditProfileImageURLResponseDTO.swift b/Projects/Domains/CreditDomain/Sources/ResponseDTO/FetchCreditProfileImageURLResponseDTO.swift new file mode 100644 index 000000000..c9eae65eb --- /dev/null +++ b/Projects/Domains/CreditDomain/Sources/ResponseDTO/FetchCreditProfileImageURLResponseDTO.swift @@ -0,0 +1,9 @@ +import Foundation + +struct FetchCreditProfileImageURLResponseDTO: Decodable { + let profileURL: String + + enum CodingKeys: String, CodingKey { + case profileURL = "profileUrl" + } +} diff --git a/Projects/Domains/CreditDomain/Sources/UseCase/FetchCreditProfileImageURLUseCaseImpl.swift b/Projects/Domains/CreditDomain/Sources/UseCase/FetchCreditProfileImageURLUseCaseImpl.swift new file mode 100644 index 000000000..e883af9fe --- /dev/null +++ b/Projects/Domains/CreditDomain/Sources/UseCase/FetchCreditProfileImageURLUseCaseImpl.swift @@ -0,0 +1,16 @@ +import CreditDomainInterface +import RxSwift + +public final class FetchCreditProfileImageURLUseCaseImpl: FetchCreditProfileImageURLUseCase { + private let creditRepository: any CreditRepository + + public init( + creditRepository: any CreditRepository + ) { + self.creditRepository = creditRepository + } + + public func execute(name: String) -> Single { + creditRepository.fetchCreditProfileImageURL(name: name) + } +} diff --git a/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileImageURLUseCaseSpy.swift b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileImageURLUseCaseSpy.swift new file mode 100644 index 000000000..c6a4d058d --- /dev/null +++ b/Projects/Domains/CreditDomain/Testing/UseCase/FetchCreditProfileImageURLUseCaseSpy.swift @@ -0,0 +1,17 @@ +import CreditDomainInterface +import RxSwift +import SongsDomainInterface + +public final class FetchCreditProfileImageURLUseCaseSpy: FetchCreditProfileImageURLUseCase { + public var callCount = 0 + public var handler: (String) -> Single = { _ in fatalError() } + + public init() {} + + public func execute( + name: String + ) -> Single { + callCount += 1 + return handler(name) + } +} diff --git a/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift b/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift index 2fe642032..0dc941da1 100644 --- a/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift +++ b/Projects/Features/CreditSongListFeature/Demo/Sources/AppDelegate.swift @@ -16,7 +16,10 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) let creditSongListTabFactory = FakeCreditSongListTabFactory() - let reactor = CreditSongListReactor(workerName: "CLTH") + let reactor = CreditSongListReactor( + workerName: "CLTH", + fetchCreditProfileImageURLUseCase: FetchCreditProfileImageURLUseCaseSpy() + ) let viewController = CreditSongListViewController( reactor: reactor, creditSongListTabFactory: creditSongListTabFactory diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/Component/CreditSongListComponent.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/Component/CreditSongListComponent.swift index f941afffe..20f827569 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/Component/CreditSongListComponent.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/Component/CreditSongListComponent.swift @@ -1,14 +1,19 @@ +import CreditDomainInterface import CreditSongListFeatureInterface import NeedleFoundation import UIKit public protocol CreditSongListDependency: Dependency { var creditSongListTabFactory: any CreditSongListTabFactory { get } + var fetchCreditProfileImageURLUseCase: any FetchCreditProfileImageURLUseCase { get } } public final class CreditSongListComponent: Component, CreditSongListFactory { public func makeViewController(workerName: String) -> UIViewController { - let reactor = CreditSongListReactor(workerName: workerName) + let reactor = CreditSongListReactor( + workerName: workerName, + fetchCreditProfileImageURLUseCase: dependency.fetchCreditProfileImageURLUseCase + ) let viewController = CreditSongListViewController( reactor: reactor, creditSongListTabFactory: dependency.creditSongListTabFactory diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListReactor.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListReactor.swift index 23eb7f0da..5966a8bd4 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListReactor.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListReactor.swift @@ -1,27 +1,59 @@ +import CreditDomainInterface import ReactorKit final class CreditSongListReactor: Reactor { - enum Action {} - enum Mutation {} + enum Action { + case viewDidLoad + } + + enum Mutation { + case updateProfileImageURL(String?) + } + struct State { var workerName: String + var profileImageURL: String? } let initialState: State internal let workerName: String + private let fetchCreditProfileImageURLUseCase: FetchCreditProfileImageURLUseCase - init(workerName: String) { + init( + workerName: String, + fetchCreditProfileImageURLUseCase: FetchCreditProfileImageURLUseCase + ) { self.initialState = .init( workerName: workerName ) self.workerName = workerName + self.fetchCreditProfileImageURLUseCase = fetchCreditProfileImageURLUseCase } func mutate(action: Action) -> Observable { - .empty() + switch action { + case .viewDidLoad: + return viewDidLoad() + } } func reduce(state: State, mutation: Mutation) -> State { - state + var newState = state + + switch mutation { + case let .updateProfileImageURL(url): + newState.profileImageURL = url + } + + return newState + } +} + +private extension CreditSongListReactor { + func viewDidLoad() -> Observable { + fetchCreditProfileImageURLUseCase + .execute(name: workerName) + .map { Mutation.updateProfileImageURL($0) } + .asObservable() } } diff --git a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListViewController.swift b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListViewController.swift index 88d955871..d0d68830c 100644 --- a/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListViewController.swift +++ b/Projects/Features/CreditSongListFeature/Sources/CreditSongList/CreditSongListViewController.swift @@ -99,6 +99,11 @@ final class CreditSongListViewController: BaseReactorViewController