diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb853a2f..b1720454 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,10 +84,10 @@ jobs: uses: actions/checkout@v4 - name: Build - run: swift build --build-tests -Xswiftc -warnings-as-errors + run: swift build --build-tests -Xswiftc -warnings-as-errors -Xswiftc -strict-concurrency=complete - name: Test - run: swift test --skip-build --filter TMDbTests + run: swift test --skip-build --filter TMDbTests -Xswiftc -strict-concurrency=complete - name: Build for Release - run: swift build -c release -Xswiftc -warnings-as-errors + run: swift build -c release -Xswiftc -warnings-as-errors -Xswiftc -strict-concurrency=complete diff --git a/README.md b/README.md index efad085a..49f64c04 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,15 @@ Add the TMDb package to your Project's Package dependencies. Create an API key from The Movie Database web site [https://www.themoviedb.org/documentation/api](https://www.themoviedb.org/documentation/api). +### Quick Start + +```swift +let tmdbClient = TMDbClient(apiKey: "") + +let moviesToDiscover = try await tmdbClient.discover.movies().results +let fightClub = try await tmdbClient.movies.details(forMovie: 550) +``` + ## Documentation Documentation and examples of usage can be found at diff --git a/Sources/TMDb/Domain/APIClient/APIClient.swift b/Sources/TMDb/Domain/APIClient/APIClient.swift index bce65509..68bddb8a 100644 --- a/Sources/TMDb/Domain/APIClient/APIClient.swift +++ b/Sources/TMDb/Domain/APIClient/APIClient.swift @@ -19,7 +19,7 @@ import Foundation -protocol APIClient { +protocol APIClient: Sendable { func perform(_ request: Request) async throws -> Request.Response diff --git a/Sources/TMDb/Domain/Services/Account/AccountService.swift b/Sources/TMDb/Domain/Services/Account/AccountService.swift index 295422f2..7ab16ae3 100644 --- a/Sources/TMDb/Domain/Services/Account/AccountService.swift +++ b/Sources/TMDb/Domain/Services/Account/AccountService.swift @@ -19,30 +19,11 @@ import Foundation -// swiftlint:disable file_length - /// /// Provides an interface for obtaining account data from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class AccountService { - - private let apiClient: any APIClient - - /// - /// Creates an account service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol AccountService: Sendable { /// /// Returns the TMDb user's account details. @@ -53,18 +34,7 @@ public final class AccountService { /// /// - Returns: The user's account details. /// - public func details(session: Session) async throws -> AccountDetails { - let request = AccountRequest(sessionID: session.sessionID) - - let accountDetails: AccountDetails - do { - accountDetails = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return accountDetails - } + func details(session: Session) async throws -> AccountDetails /// /// Returns a list of the user's favourited movies. @@ -79,28 +49,12 @@ public final class AccountService { /// /// - Returns: A list of the user's favourited movies. /// - public func favouriteMovies( - sortedBy: FavouriteSort? = nil, - page: Int? = nil, + func favouriteMovies( + sortedBy: FavouriteSort?, + page: Int?, accountID: Int, session: Session - ) async throws -> MoviePageableList { - let request = FavouriteMoviesRequest( - sortedBy: sortedBy, - page: page, - accountID: accountID, - sessionID: session.sessionID - ) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + ) async throws -> MoviePageableList /// /// Returns a list of the user's favourited TV series. @@ -115,28 +69,12 @@ public final class AccountService { /// /// - Returns: A list of the user's favourited TV series. /// - public func favouriteTVSeries( - sortedBy: FavouriteSort? = nil, - page: Int? = nil, + func favouriteTVSeries( + sortedBy: FavouriteSort?, + page: Int?, accountID: Int, session: Session - ) async throws -> TVSeriesPageableList { - let request = FavouriteTVSeriesRequest( - sortedBy: sortedBy, - page: page, - accountID: accountID, - sessionID: session.sessionID - ) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + ) async throws -> TVSeriesPageableList /// /// Adds a movie to a user's favourites. @@ -148,15 +86,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func addFavourite(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { - try await addFavourite( - showType: .movie, - showID: movieID, - isFavourite: true, - accountID: accountID, - session: session - ) - } + func addFavourite( + movie movieID: Movie.ID, + accountID: Int, + session: Session + ) async throws /// /// Removes a movie from a user's favourites. @@ -168,15 +102,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func removeFavourite(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { - try await addFavourite( - showType: .movie, - showID: movieID, - isFavourite: false, - accountID: accountID, - session: session - ) - } + func removeFavourite( + movie movieID: Movie.ID, + accountID: Int, + session: Session + ) async throws /// /// Adds a TV series to a user's favourites. @@ -188,15 +118,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func addFavourite(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { - try await addFavourite( - showType: .tvSeries, - showID: tvSeriesID, - isFavourite: true, - accountID: accountID, - session: session - ) - } + func addFavourite( + tvSeries tvSeriesID: TVSeries.ID, + accountID: Int, + session: Session + ) async throws /// /// Removes a TV series from a user's favourites. @@ -208,15 +134,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func removeFavourite(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { - try await addFavourite( - showType: .tvSeries, - showID: tvSeriesID, - isFavourite: false, - accountID: accountID, - session: session - ) - } + func removeFavourite( + tvSeries tvSeriesID: TVSeries.ID, + accountID: Int, + session: Session + ) async throws /// /// Returns a list of movies in the user's watchlist. @@ -231,28 +153,12 @@ public final class AccountService { /// /// - Returns: A list of movies in the user's watchlist. /// - public func movieWatchlist( - sortedBy: WatchlistSort? = nil, - page: Int? = nil, + func movieWatchlist( + sortedBy: WatchlistSort?, + page: Int?, accountID: Int, session: Session - ) async throws -> MoviePageableList { - let request = MovieWatchlistRequest( - sortedBy: sortedBy, - page: page, - accountID: accountID, - sessionID: session.sessionID - ) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + ) async throws -> MoviePageableList /// /// Returns a list of TV series in the user's watchlist. @@ -267,28 +173,12 @@ public final class AccountService { /// /// - Returns: A list of TV series in the user's watchlist. /// - public func tvSeriesWatchlist( - sortedBy: WatchlistSort? = nil, - page: Int? = nil, + func tvSeriesWatchlist( + sortedBy: WatchlistSort?, + page: Int?, accountID: Int, session: Session - ) async throws -> TVSeriesPageableList { - let request = TVSeriesWatchlistRequest( - sortedBy: sortedBy, - page: page, - accountID: accountID, - sessionID: session.sessionID - ) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + ) async throws -> TVSeriesPageableList /// /// Adds a movie to a user's watchlist. @@ -300,15 +190,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func addToWatchlist(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { - try await addToWatchlist( - showType: .movie, - showID: movieID, - isInWatchlist: true, - accountID: accountID, - session: session - ) - } + func addToWatchlist( + movie movieID: Movie.ID, + accountID: Int, + session: Session + ) async throws /// /// Removes a movie from a user's watchlist. @@ -320,15 +206,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func removeFromWatchlist(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { - try await addToWatchlist( - showType: .movie, - showID: movieID, - isInWatchlist: false, - accountID: accountID, - session: session - ) - } + func removeFromWatchlist( + movie movieID: Movie.ID, + accountID: Int, + session: Session + ) async throws /// /// Adds a TV series to a user's watchlist. @@ -340,15 +222,11 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func addToWatchlist(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { - try await addToWatchlist( - showType: .tvSeries, - showID: tvSeriesID, - isInWatchlist: true, - accountID: accountID, - session: session - ) - } + func addToWatchlist( + tvSeries tvSeriesID: TVSeries.ID, + accountID: Int, + session: Session + ) async throws /// /// Removes a TV series from a user's watchlist. @@ -360,62 +238,70 @@ public final class AccountService { /// /// - Throws: TMDb error ``TMDbError``. /// - public func removeFromWatchlist(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { - try await addToWatchlist( - showType: .tvSeries, - showID: tvSeriesID, - isInWatchlist: false, - accountID: accountID, - session: session - ) - } + func removeFromWatchlist( + tvSeries tvSeriesID: TVSeries.ID, + accountID: Int, + session: Session + ) async throws } -extension AccountService { +public extension AccountService { - private func addFavourite( - showType: ShowType, - showID: Show.ID, - isFavourite: Bool, + func favouriteMovies( + sortedBy: FavouriteSort? = nil, + page: Int? = nil, accountID: Int, session: Session - ) async throws { - let request = AddFavouriteRequest( - showType: showType, - showID: showID, - isFavourite: isFavourite, + ) async throws -> MoviePageableList { + try await favouriteMovies( + sortedBy: sortedBy, + page: page, accountID: accountID, - sessionID: session.sessionID + session: session ) + } - do { - _ = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func favouriteTVSeries( + sortedBy: FavouriteSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> TVSeriesPageableList { + try await favouriteTVSeries( + sortedBy: sortedBy, + page: page, + accountID: accountID, + session: session + ) } - private func addToWatchlist( - showType: ShowType, - showID: Show.ID, - isInWatchlist: Bool, + func movieWatchlist( + sortedBy: WatchlistSort? = nil, + page: Int? = nil, accountID: Int, session: Session - ) async throws { - let request = AddToWatchlistRequest( - showType: showType, - showID: showID, - isInWatchlist: isInWatchlist, + ) async throws -> MoviePageableList { + try await movieWatchlist( + sortedBy: sortedBy, + page: page, accountID: accountID, - sessionID: session.sessionID + session: session ) + } - do { - _ = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func tvSeriesWatchlist( + sortedBy: WatchlistSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> TVSeriesPageableList { + try await tvSeriesWatchlist( + sortedBy: sortedBy, + page: page, + accountID: accountID, + session: session + ) } } diff --git a/Sources/TMDb/Domain/Services/Account/FavouriteSort.swift b/Sources/TMDb/Domain/Services/Account/Sorts/FavouriteSort.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Account/FavouriteSort.swift rename to Sources/TMDb/Domain/Services/Account/Sorts/FavouriteSort.swift diff --git a/Sources/TMDb/Domain/Services/Account/WatchlistSort.swift b/Sources/TMDb/Domain/Services/Account/Sorts/WatchlistSort.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Account/WatchlistSort.swift rename to Sources/TMDb/Domain/Services/Account/Sorts/WatchlistSort.swift diff --git a/Sources/TMDb/Domain/Services/Account/TMDbAccountService.swift b/Sources/TMDb/Domain/Services/Account/TMDbAccountService.swift new file mode 100644 index 00000000..44a102c1 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Account/TMDbAccountService.swift @@ -0,0 +1,264 @@ +// +// TMDbAccountService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbAccountService: AccountService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details(session: Session) async throws -> AccountDetails { + let request = AccountRequest(sessionID: session.sessionID) + + let accountDetails: AccountDetails + do { + accountDetails = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return accountDetails + } + + func favouriteMovies( + sortedBy: FavouriteSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> MoviePageableList { + let request = FavouriteMoviesRequest( + sortedBy: sortedBy, + page: page, + accountID: accountID, + sessionID: session.sessionID + ) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func favouriteTVSeries( + sortedBy: FavouriteSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> TVSeriesPageableList { + let request = FavouriteTVSeriesRequest( + sortedBy: sortedBy, + page: page, + accountID: accountID, + sessionID: session.sessionID + ) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func addFavourite(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { + try await addFavourite( + showType: .movie, + showID: movieID, + isFavourite: true, + accountID: accountID, + session: session + ) + } + + func removeFavourite(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { + try await addFavourite( + showType: .movie, + showID: movieID, + isFavourite: false, + accountID: accountID, + session: session + ) + } + + func addFavourite(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { + try await addFavourite( + showType: .tvSeries, + showID: tvSeriesID, + isFavourite: true, + accountID: accountID, + session: session + ) + } + + func removeFavourite(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { + try await addFavourite( + showType: .tvSeries, + showID: tvSeriesID, + isFavourite: false, + accountID: accountID, + session: session + ) + } + + func movieWatchlist( + sortedBy: WatchlistSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> MoviePageableList { + let request = MovieWatchlistRequest( + sortedBy: sortedBy, + page: page, + accountID: accountID, + sessionID: session.sessionID + ) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func tvSeriesWatchlist( + sortedBy: WatchlistSort? = nil, + page: Int? = nil, + accountID: Int, + session: Session + ) async throws -> TVSeriesPageableList { + let request = TVSeriesWatchlistRequest( + sortedBy: sortedBy, + page: page, + accountID: accountID, + sessionID: session.sessionID + ) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func addToWatchlist(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { + try await addToWatchlist( + showType: .movie, + showID: movieID, + isInWatchlist: true, + accountID: accountID, + session: session + ) + } + + func removeFromWatchlist(movie movieID: Movie.ID, accountID: Int, session: Session) async throws { + try await addToWatchlist( + showType: .movie, + showID: movieID, + isInWatchlist: false, + accountID: accountID, + session: session + ) + } + + func addToWatchlist(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { + try await addToWatchlist( + showType: .tvSeries, + showID: tvSeriesID, + isInWatchlist: true, + accountID: accountID, + session: session + ) + } + + func removeFromWatchlist(tvSeries tvSeriesID: TVSeries.ID, accountID: Int, session: Session) async throws { + try await addToWatchlist( + showType: .tvSeries, + showID: tvSeriesID, + isInWatchlist: false, + accountID: accountID, + session: session + ) + } + +} + +extension TMDbAccountService { + + private func addFavourite( + showType: ShowType, + showID: Show.ID, + isFavourite: Bool, + accountID: Int, + session: Session + ) async throws { + let request = AddFavouriteRequest( + showType: showType, + showID: showID, + isFavourite: isFavourite, + accountID: accountID, + sessionID: session.sessionID + ) + + do { + _ = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + } + + private func addToWatchlist( + showType: ShowType, + showID: Show.ID, + isInWatchlist: Bool, + accountID: Int, + session: Session + ) async throws { + let request = AddToWatchlistRequest( + showType: showType, + showID: showID, + isInWatchlist: isInWatchlist, + accountID: accountID, + sessionID: session.sessionID + ) + + do { + _ = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + } + +} diff --git a/Sources/TMDb/Domain/Services/Authentication/AuthenticateURLBuilding.swift b/Sources/TMDb/Domain/Services/Authentication/AuthenticateURLBuilding.swift index a05c8d46..8ded970c 100644 --- a/Sources/TMDb/Domain/Services/Authentication/AuthenticateURLBuilding.swift +++ b/Sources/TMDb/Domain/Services/Authentication/AuthenticateURLBuilding.swift @@ -19,7 +19,7 @@ import Foundation -protocol AuthenticateURLBuilding { +protocol AuthenticateURLBuilding: Sendable { func authenticateURL(with requestToken: String) -> URL diff --git a/Sources/TMDb/Domain/Services/Authentication/AuthenticationService.swift b/Sources/TMDb/Domain/Services/Authentication/AuthenticationService.swift index ad5518a7..81a44319 100644 --- a/Sources/TMDb/Domain/Services/Authentication/AuthenticationService.swift +++ b/Sources/TMDb/Domain/Services/Authentication/AuthenticationService.swift @@ -26,30 +26,7 @@ import Foundation /// [TMDb API - How do I generate a session ID?](https://developer.themoviedb.org/reference/authentication-how-do-i-generate-a-session-id) /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class AuthenticationService { - - private let apiClient: any APIClient - private let authenticateURLBuilder: any AuthenticateURLBuilding - - /// - /// Creates an authentication service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.authAPIClient(configuration: configuration), - authenticateURLBuilder: TMDbFactory.authenticateURLBuilder() - ) - } - - init( - apiClient: some APIClient, - authenticateURLBuilder: some AuthenticateURLBuilding - ) { - self.apiClient = apiClient - self.authenticateURLBuilder = authenticateURLBuilder - } +public protocol AuthenticationService: Sendable { /// /// Creates a guest session with TMDb. @@ -66,18 +43,7 @@ public final class AuthenticationService { /// /// - Returns: A guest session. /// - public func guestSession() async throws -> GuestSession { - let request = CreateGuestSessionRequest() - - let session: GuestSession - do { - session = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return session - } + func guestSession() async throws -> GuestSession /// /// Creates an intermediate request token that can be used to validate a TMDb user login. @@ -91,18 +57,7 @@ public final class AuthenticationService { /// /// - Returns: An intermediate request token. /// - public func requestToken() async throws -> Token { - let request = CreateRequestTokenRequest() - - let token: Token - do { - token = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return token - } + func requestToken() async throws -> Token /// /// Builds the URL used for the user to authenticate with after requesting an intermediate request token. @@ -115,11 +70,7 @@ public final class AuthenticationService { /// /// - Returns: An authenticate URL. /// - public func authenticateURL(for token: Token, redirectURL: URL? = nil) -> URL { - let url = authenticateURLBuilder.authenticateURL(with: token.requestToken, redirectURL: redirectURL) - - return url - } + func authenticateURL(for token: Token, redirectURL: URL?) -> URL /// /// Creates a TMDb session with a valid request token. @@ -133,18 +84,7 @@ public final class AuthenticationService { /// /// - Returns: A TMDb session. /// - public func createSession(withToken token: Token) async throws -> Session { - let request = CreateSessionRequest(requestToken: token.requestToken) - - let session: Session - do { - session = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return session - } + func createSession(withToken token: Token) async throws -> Session /// /// Creates a TMDb session using a user's username and password. @@ -156,26 +96,7 @@ public final class AuthenticationService { /// /// - Returns: A TMDb session. /// - public func createSession(withCredential credential: Credential) async throws -> Session { - let token = try await requestToken() - - let request = ValidateTokenWithLoginRequest( - username: credential.username, - password: credential.password, - requestToken: token.requestToken - ) - - let validatedToken: Token - do { - validatedToken = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - let session = try await createSession(withToken: validatedToken) - - return session - } + func createSession(withCredential credential: Credential) async throws -> Session /// /// Deletes a user's session on TMDb. @@ -187,35 +108,21 @@ public final class AuthenticationService { /// - Returns: Whether or not the session was successfully delete. /// @discardableResult - public func deleteSession(_ session: Session) async throws -> Bool { - let request = DeleteSessionRequest(sessionID: session.sessionID) - - let result: SuccessResult - do { - result = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return result.success - } + func deleteSession(_ session: Session) async throws -> Bool /// /// Validates the configured API key. /// /// - Returns: Whether or not the API key is valid. /// - public func validateKey() async throws -> Bool { - let request = ValidateKeyRequest() + func validateKey() async throws -> Bool + +} - let result: SuccessResult - do { - result = try await apiClient.perform(request) - } catch { - return false - } +public extension AuthenticationService { - return result.success + func authenticateURL(for token: Token, redirectURL: URL? = nil) -> URL { + authenticateURL(for: token, redirectURL: redirectURL) } } diff --git a/Sources/TMDb/Domain/Services/Authentication/TMDbAuthenticationService.swift b/Sources/TMDb/Domain/Services/Authentication/TMDbAuthenticationService.swift new file mode 100644 index 00000000..60f1ab2f --- /dev/null +++ b/Sources/TMDb/Domain/Services/Authentication/TMDbAuthenticationService.swift @@ -0,0 +1,129 @@ +// +// TMDbAuthenticationService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbAuthenticationService: AuthenticationService { + + private let apiClient: any APIClient + private let authenticateURLBuilder: any AuthenticateURLBuilding + + init( + apiClient: some APIClient, + authenticateURLBuilder: some AuthenticateURLBuilding + ) { + self.apiClient = apiClient + self.authenticateURLBuilder = authenticateURLBuilder + } + + func guestSession() async throws -> GuestSession { + let request = CreateGuestSessionRequest() + + let session: GuestSession + do { + session = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return session + } + + func requestToken() async throws -> Token { + let request = CreateRequestTokenRequest() + + let token: Token + do { + token = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return token + } + + func authenticateURL(for token: Token, redirectURL: URL? = nil) -> URL { + let url = authenticateURLBuilder.authenticateURL(with: token.requestToken, redirectURL: redirectURL) + + return url + } + + func createSession(withToken token: Token) async throws -> Session { + let request = CreateSessionRequest(requestToken: token.requestToken) + + let session: Session + do { + session = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return session + } + + func createSession(withCredential credential: Credential) async throws -> Session { + let token = try await requestToken() + + let request = ValidateTokenWithLoginRequest( + username: credential.username, + password: credential.password, + requestToken: token.requestToken + ) + + let validatedToken: Token + do { + validatedToken = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + let session = try await createSession(withToken: validatedToken) + + return session + } + + @discardableResult + func deleteSession(_ session: Session) async throws -> Bool { + let request = DeleteSessionRequest(sessionID: session.sessionID) + + let result: SuccessResult + do { + result = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return result.success + } + + func validateKey() async throws -> Bool { + let request = ValidateKeyRequest() + + let result: SuccessResult + do { + result = try await apiClient.perform(request) + } catch { + return false + } + + return result.success + } + +} diff --git a/Sources/TMDb/Domain/Services/Certifications/CertificationService.swift b/Sources/TMDb/Domain/Services/Certifications/CertificationService.swift index 2ccdb1a0..9c82684f 100644 --- a/Sources/TMDb/Domain/Services/Certifications/CertificationService.swift +++ b/Sources/TMDb/Domain/Services/Certifications/CertificationService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining certification data from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class CertificationService { - - private let apiClient: any APIClient - - /// - /// Creates a certificate service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol CertificationService: Sendable { /// /// Returns an up to date list of the officially supported movie certifications on TMDB. @@ -51,18 +34,7 @@ public final class CertificationService { /// /// - Returns: A dictionary of movie certifications. /// - public func movieCertifications() async throws -> [String: [Certification]] { - let request = MovieCertificationsRequest() - - let certifications: Certifications - do { - certifications = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return certifications.certifications - } + func movieCertifications() async throws -> [String: [Certification]] /// /// Returns an up to date list of the officially supported TV certifications on TMDB. @@ -73,17 +45,6 @@ public final class CertificationService { /// /// - Returns: A dictionary of TV series certifications. /// - public func tvSeriesCertifications() async throws -> [String: [Certification]] { - let request = TVSeriesCertificationsRequest() - - let certifications: Certifications - do { - certifications = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return certifications.certifications - } + func tvSeriesCertifications() async throws -> [String: [Certification]] } diff --git a/Sources/TMDb/Domain/Services/Certifications/TMDbCertificationService.swift b/Sources/TMDb/Domain/Services/Certifications/TMDbCertificationService.swift new file mode 100644 index 00000000..72666cdf --- /dev/null +++ b/Sources/TMDb/Domain/Services/Certifications/TMDbCertificationService.swift @@ -0,0 +1,57 @@ +// +// TMDbCertificationService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbCertificationService: CertificationService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func movieCertifications() async throws -> [String: [Certification]] { + let request = MovieCertificationsRequest() + + let certifications: Certifications + do { + certifications = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return certifications.certifications + } + + func tvSeriesCertifications() async throws -> [String: [Certification]] { + let request = TVSeriesCertificationsRequest() + + let certifications: Certifications + do { + certifications = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return certifications.certifications + } + +} diff --git a/Sources/TMDb/Domain/Services/Company/CompanyService.swift b/Sources/TMDb/Domain/Services/Companies/CompanyService.swift similarity index 58% rename from Sources/TMDb/Domain/Services/Company/CompanyService.swift rename to Sources/TMDb/Domain/Services/Companies/CompanyService.swift index aa964ec8..5d849c29 100644 --- a/Sources/TMDb/Domain/Services/Company/CompanyService.swift +++ b/Sources/TMDb/Domain/Services/Companies/CompanyService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining company data from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class CompanyService { - - private let apiClient: any APIClient - - /// - /// Creates a company service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol CompanyService: Sendable { /// /// Returns a company's details @@ -54,17 +37,6 @@ public final class CompanyService { /// /// - Returns: Matching company. /// - public func details(forCompany id: Company.ID) async throws -> Company { - let request = CompanyDetailsRequest(id: id) - - let company: Company - do { - company = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return company - } + func details(forCompany id: Company.ID) async throws -> Company } diff --git a/Sources/TMDb/Domain/Services/Company/Requests/CompanyDetailsRequest.swift b/Sources/TMDb/Domain/Services/Companies/Requests/CompanyDetailsRequest.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Company/Requests/CompanyDetailsRequest.swift rename to Sources/TMDb/Domain/Services/Companies/Requests/CompanyDetailsRequest.swift diff --git a/Sources/TMDb/Domain/ConfigurationProvider/ConfigurationProviding.swift b/Sources/TMDb/Domain/Services/Companies/TMDbCompanyService.swift similarity index 51% rename from Sources/TMDb/Domain/ConfigurationProvider/ConfigurationProviding.swift rename to Sources/TMDb/Domain/Services/Companies/TMDbCompanyService.swift index 02c02e6c..6f3a72c2 100644 --- a/Sources/TMDb/Domain/ConfigurationProvider/ConfigurationProviding.swift +++ b/Sources/TMDb/Domain/Services/Companies/TMDbCompanyService.swift @@ -1,5 +1,5 @@ // -// ConfigurationProviding.swift +// TMDbCompanyService.swift // TMDb // // Copyright © 2024 Adam Young. @@ -19,21 +19,26 @@ import Foundation -/// -/// An interface to provide configuration for service. -/// -/// Create an API key at [https://www.themoviedb.org/documentation/api](https://www.themoviedb.org/documentation/api). -/// -public protocol ConfigurationProviding { - - /// - /// TMDb API key. - /// - var apiKey: String { get } - - /// - /// The HTTP client adapter for making HTTP requests. - /// - var httpClient: any HTTPClient { get } +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbCompanyService: CompanyService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details(forCompany id: Company.ID) async throws -> Company { + let request = CompanyDetailsRequest(id: id) + + let company: Company + do { + company = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return company + } } diff --git a/Sources/TMDb/Domain/Services/Configuration/ConfigurationService.swift b/Sources/TMDb/Domain/Services/Configuration/ConfigurationService.swift index aa3c1e74..753e65c1 100644 --- a/Sources/TMDb/Domain/Services/Configuration/ConfigurationService.swift +++ b/Sources/TMDb/Domain/Services/Configuration/ConfigurationService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining configuration data from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class ConfigurationService { - - private let apiClient: any APIClient - - /// - /// Creates a configuration service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol ConfigurationService: Sendable { /// /// Returns the TMDb API system wide configuration information. The result is cached, so there is no overhead in @@ -52,18 +35,7 @@ public final class ConfigurationService { /// /// - Returns: The API configuration. /// - public func apiConfiguration() async throws -> APIConfiguration { - let request = APIConfigurationRequest() - - let apiConfiguration: APIConfiguration - do { - apiConfiguration = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return apiConfiguration - } + func apiConfiguration() async throws -> APIConfiguration /// /// Returns the list of countries used throughout TMDb. @@ -77,18 +49,7 @@ public final class ConfigurationService { /// /// - Returns: Countries used throughout TMDb, /// - public func countries(language: String? = nil) async throws -> [Country] { - let request = CountriesConfigurationRequest(language: language) - - let countries: [Country] - do { - countries = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return countries - } + func countries(language: String?) async throws -> [Country] /// /// Returns a list of the jobs and departments used on TMDb. @@ -99,19 +60,7 @@ public final class ConfigurationService { /// /// - Returns: Jobs and departments used on TMDb. /// - public func jobsByDepartment() async throws -> [Department] { - let request = JobsConfigurationRequest() - - let departments: [Department] - do { - departments = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return departments - } - + func jobsByDepartment() async throws -> [Department] /// /// Returns the list of languages (ISO 639-1 tags) used throughout TMDb. /// @@ -121,17 +70,14 @@ public final class ConfigurationService { /// /// - Returns: Languages used throughout TMDb. /// - public func languages() async throws -> [Language] { - let request = LanguaguesConfigurationRequest() + func languages() async throws -> [Language] + +} - let languages: [Language] - do { - languages = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } +public extension ConfigurationService { - return languages + func countries(language: String? = nil) async throws -> [Country] { + try await countries(language: language) } } diff --git a/Sources/TMDb/Domain/Services/Configuration/TMDbConfigurationService.swift b/Sources/TMDb/Domain/Services/Configuration/TMDbConfigurationService.swift new file mode 100644 index 00000000..e0af2557 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Configuration/TMDbConfigurationService.swift @@ -0,0 +1,83 @@ +// +// TMDbConfigurationService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbConfigurationService: ConfigurationService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func apiConfiguration() async throws -> APIConfiguration { + let request = APIConfigurationRequest() + + let apiConfiguration: APIConfiguration + do { + apiConfiguration = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return apiConfiguration + } + + func countries(language: String? = nil) async throws -> [Country] { + let request = CountriesConfigurationRequest(language: language) + + let countries: [Country] + do { + countries = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return countries + } + + func jobsByDepartment() async throws -> [Department] { + let request = JobsConfigurationRequest() + + let departments: [Department] + do { + departments = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return departments + } + + func languages() async throws -> [Language] { + let request = LanguaguesConfigurationRequest() + + let languages: [Language] + do { + languages = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return languages + } + +} diff --git a/Sources/TMDb/Domain/Services/Discover/DiscoverService.swift b/Sources/TMDb/Domain/Services/Discover/DiscoverService.swift index 8e194826..92beb71e 100644 --- a/Sources/TMDb/Domain/Services/Discover/DiscoverService.swift +++ b/Sources/TMDb/Domain/Services/Discover/DiscoverService.swift @@ -1,46 +1,8 @@ -// -// DiscoverService.swift -// TMDb -// -// Copyright © 2024 Adam Young. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an AS IS BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - /// /// Provides an interface for discovering movies and TV series from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class DiscoverService { - - private let apiClient: any APIClient - - /// - /// Creates a discover service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol DiscoverService: Sendable { /// /// Returns movies to be discovered. @@ -59,28 +21,12 @@ public final class DiscoverService { /// /// - Returns: Matching movies as a pageable list. /// - public func movies( - filter: DiscoverMovieFilter? = nil, - sortedBy: MovieSort? = nil, - page: Int? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = DiscoverMoviesRequest( - people: filter?.people, - sortedBy: sortedBy, - page: page, - language: language - ) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func movies( + filter: DiscoverMovieFilter?, + sortedBy: MovieSort?, + page: Int?, + language: String? + ) async throws -> MoviePageableList /// /// Returns TV series to be discovered. @@ -98,21 +44,31 @@ public final class DiscoverService { /// /// - Returns: Matching TV series as a pageable list. /// - public func tvSeries( + func tvSeries( + sortedBy: TVSeriesSort?, + page: Int?, + language: String? + ) async throws -> TVSeriesPageableList + +} + +public extension DiscoverService { + + func movies( + filter: DiscoverMovieFilter? = nil, + sortedBy: MovieSort? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await movies(filter: filter, sortedBy: sortedBy, page: page, language: language) + } + + func tvSeries( sortedBy: TVSeriesSort? = nil, page: Int? = nil, language: String? = nil ) async throws -> TVSeriesPageableList { - let request = DiscoverTVSeriesRequest(sortedBy: sortedBy, page: page, language: language) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList + try await tvSeries(sortedBy: sortedBy, page: page, language: language) } } diff --git a/Sources/TMDb/Domain/Services/Discover/DiscoverMovieFilter.swift b/Sources/TMDb/Domain/Services/Discover/Filters/DiscoverMovieFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Discover/DiscoverMovieFilter.swift rename to Sources/TMDb/Domain/Services/Discover/Filters/DiscoverMovieFilter.swift diff --git a/Sources/TMDb/Domain/Services/Discover/MovieSort.swift b/Sources/TMDb/Domain/Services/Discover/Sorts/MovieSort.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Discover/MovieSort.swift rename to Sources/TMDb/Domain/Services/Discover/Sorts/MovieSort.swift diff --git a/Sources/TMDb/Domain/Services/Discover/TVSeriesSort.swift b/Sources/TMDb/Domain/Services/Discover/Sorts/TVSeriesSort.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Discover/TVSeriesSort.swift rename to Sources/TMDb/Domain/Services/Discover/Sorts/TVSeriesSort.swift diff --git a/Sources/TMDb/Domain/Services/Discover/TMDbDiscoverService.swift b/Sources/TMDb/Domain/Services/Discover/TMDbDiscoverService.swift new file mode 100644 index 00000000..6e91b331 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Discover/TMDbDiscoverService.swift @@ -0,0 +1,71 @@ +// +// TMDbDiscoverService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbDiscoverService: DiscoverService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func movies( + filter: DiscoverMovieFilter? = nil, + sortedBy: MovieSort? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = DiscoverMoviesRequest( + people: filter?.people, + sortedBy: sortedBy, + page: page, + language: language + ) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func tvSeries( + sortedBy: TVSeriesSort? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + let request = DiscoverTVSeriesRequest(sortedBy: sortedBy, page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + +} diff --git a/Sources/TMDb/Domain/Services/Genres/GenreService.swift b/Sources/TMDb/Domain/Services/Genres/GenreService.swift index 057eca75..12aaf7ad 100644 --- a/Sources/TMDb/Domain/Services/Genres/GenreService.swift +++ b/Sources/TMDb/Domain/Services/Genres/GenreService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining movie and TV series genres from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class GenreService { - - private let apiClient: any APIClient - - /// - /// Creates a genre service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol GenreService: Sendable { /// /// Returns the list of official genres for movies. @@ -54,18 +37,7 @@ public final class GenreService { /// /// - Returns: A list of genres. /// - public func movieGenres(language: String? = nil) async throws -> [Genre] { - let request = MovieGenresRequest(language: language) - - let genreList: GenreList - do { - genreList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return genreList.genres - } + func movieGenres(language: String?) async throws -> [Genre] /// /// Returns the list of official genres for TV series. @@ -79,17 +51,18 @@ public final class GenreService { /// /// - Returns: A list of genres. /// - public func tvSeriesGenres(language: String? = nil) async throws -> [Genre] { - let request = TVSeriesGenresRequest(language: language) + func tvSeriesGenres(language: String?) async throws -> [Genre] - let genreList: GenreList - do { - genreList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } +} + +public extension GenreService { + + func movieGenres(language: String? = nil) async throws -> [Genre] { + try await movieGenres(language: language) + } - return genreList.genres + func tvSeriesGenres(language: String? = nil) async throws -> [Genre] { + try await tvSeriesGenres(language: language) } } diff --git a/Sources/TMDb/Domain/Services/Genres/TMDbGenreService.swift b/Sources/TMDb/Domain/Services/Genres/TMDbGenreService.swift new file mode 100644 index 00000000..b5346a44 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Genres/TMDbGenreService.swift @@ -0,0 +1,60 @@ +// +// TMDbGenreService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// Provides an interface for obtaining movie and TV series genres from TMDb. +/// +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbGenreService: GenreService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func movieGenres(language: String? = nil) async throws -> [Genre] { + let request = MovieGenresRequest(language: language) + + let genreList: GenreList + do { + genreList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return genreList.genres + } + + func tvSeriesGenres(language: String? = nil) async throws -> [Genre] { + let request = TVSeriesGenresRequest(language: language) + + let genreList: GenreList + do { + genreList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return genreList.genres + } + +} diff --git a/Sources/TMDb/Domain/Services/Movies/MovieImageFilter.swift b/Sources/TMDb/Domain/Services/Movies/Filters/MovieImageFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Movies/MovieImageFilter.swift rename to Sources/TMDb/Domain/Services/Movies/Filters/MovieImageFilter.swift diff --git a/Sources/TMDb/Domain/Services/Movies/MovieVideoFilter.swift b/Sources/TMDb/Domain/Services/Movies/Filters/MovieVideoFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Movies/MovieVideoFilter.swift rename to Sources/TMDb/Domain/Services/Movies/Filters/MovieVideoFilter.swift diff --git a/Sources/TMDb/Domain/Services/Movies/MovieService.swift b/Sources/TMDb/Domain/Services/Movies/MovieService.swift index c6a0f009..bb434089 100644 --- a/Sources/TMDb/Domain/Services/Movies/MovieService.swift +++ b/Sources/TMDb/Domain/Services/Movies/MovieService.swift @@ -19,30 +19,11 @@ import Foundation -// swiftlint:disable file_length - /// /// Provides an interface for obtaining movies from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class MovieService { - - private let apiClient: any APIClient - - /// - /// Creates a movie service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol MovieService: Sendable { /// /// Returns the primary information about a movie. @@ -57,18 +38,7 @@ public final class MovieService { /// /// - Returns: The matching movie. /// - public func details(forMovie id: Movie.ID, language: String? = nil) async throws -> Movie { - let request = MovieRequest(id: id, language: language) - - let movie: Movie - do { - movie = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movie - } + func details(forMovie id: Movie.ID, language: String?) async throws -> Movie /// /// Returns the cast and crew of a movie. @@ -83,18 +53,7 @@ public final class MovieService { /// /// - Returns: Credits for the matching movie. /// - public func credits(forMovie movieID: Movie.ID, language: String? = nil) async throws -> ShowCredits { - let request = MovieCreditsRequest(id: movieID, language: language) - - let credits: ShowCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + func credits(forMovie movieID: Movie.ID, language: String?) async throws -> ShowCredits /// /// Returns the user reviews for a movie. @@ -112,22 +71,7 @@ public final class MovieService { /// /// - Returns: Reviews for the matching movie as a pageable list. /// - public func reviews( - forMovie movieID: Movie.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> ReviewPageableList { - let request = MovieReviewsRequest(id: movieID, page: page, language: language) - - let reviewList: ReviewPageableList - do { - reviewList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return reviewList - } + func reviews(forMovie movieID: Movie.ID, page: Int?, language: String?) async throws -> ReviewPageableList /// /// Returns the images that belong to a movie. @@ -142,18 +86,7 @@ public final class MovieService { /// /// - Returns: Collection of images for the matching movie. /// - public func images(forMovie movieID: Movie.ID, filter: MovieImageFilter? = nil) async throws -> ImageCollection { - let request = MovieImagesRequest(id: movieID, languages: filter?.languages) - - let imageCollection: ImageCollection - do { - imageCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return imageCollection - } + func images(forMovie movieID: Movie.ID, filter: MovieImageFilter?) async throws -> ImageCollection /// /// Returns the videos that have been added to a movie. @@ -168,18 +101,7 @@ public final class MovieService { /// /// - Returns: Collection of videos for the matching movie. /// - public func videos(forMovie movieID: Movie.ID, filter: MovieVideoFilter? = nil) async throws -> VideoCollection { - let request = MovieVideosRequest(id: movieID, languages: filter?.languages) - - let videoCollection: VideoCollection - do { - videoCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return videoCollection - } + func videos(forMovie movieID: Movie.ID, filter: MovieVideoFilter?) async throws -> VideoCollection /// /// Returns a list of recommended movies for a movie. @@ -197,22 +119,7 @@ public final class MovieService { /// /// - Returns: Recommended movies for the matching movie as a pageable list. /// - public func recommendations( - forMovie movieID: Movie.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = MovieRecommendationsRequest(id: movieID, page: page, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func recommendations(forMovie movieID: Movie.ID, page: Int?, language: String?) async throws -> MoviePageableList /// /// Returns a list of similar movies for a movie. @@ -232,22 +139,7 @@ public final class MovieService { /// /// - Returns: Similar movies for the matching movie as a pageable list. /// - public func similar( - toMovie movieID: Movie.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = SimilarMoviesRequest(id: movieID, page: page, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func similar(toMovie movieID: Movie.ID, page: Int?, language: String?) async throws -> MoviePageableList /// /// Returns a list of currently playing movies. @@ -265,22 +157,7 @@ public final class MovieService { /// /// - Returns: Now playing movies as a pageable list. /// - public func nowPlaying( - page: Int? = nil, - country: String? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = MoviesNowPlayingRequest(page: page, country: country, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func nowPlaying(page: Int?, country: String?, language: String?) async throws -> MoviePageableList /// /// Returns a list of current popular movies. @@ -298,22 +175,7 @@ public final class MovieService { /// /// - Returns: Current popular movies as a pageable list. /// - public func popular( - page: Int? = nil, - country: String? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = PopularMoviesRequest(page: page, country: country, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func popular(page: Int?, country: String?, language: String?) async throws -> MoviePageableList /// /// Returns a list of top rated movies. @@ -331,22 +193,7 @@ public final class MovieService { /// /// - Returns: Top rated movies as a pageable list. /// - public func topRated( - page: Int? = nil, - country: String? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = TopRatedMoviesRequest(page: page, country: country, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func topRated(page: Int?, country: String?, language: String?) async throws -> MoviePageableList /// /// Returns a list of upcoming movies. @@ -364,22 +211,7 @@ public final class MovieService { /// /// - Returns: Upcoming movies as a pageable list. /// - public func upcoming( - page: Int? = nil, - country: String? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = UpcomingMoviesRequest(page: page, country: country, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func upcoming(page: Int?, country: String?, language: String?) async throws -> MoviePageableList /// /// Returns watch providers for a movie @@ -396,18 +228,7 @@ public final class MovieService { /// /// - Returns: Watch providers for movie in current region. /// - public func watchProviders(forMovie movieID: Movie.ID, country: String = "US") async throws -> ShowWatchProvider? { - let request = MovieWatchProvidersRequest(id: movieID) - - let result: ShowWatchProviderResult - do { - result = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return result.results[country] - } + func watchProviders(forMovie movieID: Movie.ID, country: String) async throws -> ShowWatchProvider? /// /// Returns a collection of media databases and social links for a movie. @@ -419,17 +240,86 @@ public final class MovieService { /// /// - Returns: A collection of external links for the specificed movie. /// - public func externalLinks(forMovie movieID: Movie.ID) async throws -> MovieExternalLinksCollection { - let request = MovieExternalLinksRequest(id: movieID) + func externalLinks(forMovie movieID: Movie.ID) async throws -> MovieExternalLinksCollection + +} + +public extension MovieService { + + func details(forMovie id: Movie.ID, language: String? = nil) async throws -> Movie { + try await details(forMovie: id, language: language) + } + + func credits(forMovie movieID: Movie.ID, language: String? = nil) async throws -> ShowCredits { + try await credits(forMovie: movieID, language: language) + } + + func reviews( + forMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> ReviewPageableList { + try await reviews(forMovie: movieID, page: page, language: language) + } + + func images(forMovie movieID: Movie.ID, filter: MovieImageFilter? = nil) async throws -> ImageCollection { + try await images(forMovie: movieID, filter: filter) + } + + func videos(forMovie movieID: Movie.ID, filter: MovieVideoFilter? = nil) async throws -> VideoCollection { + try await videos(forMovie: movieID, filter: filter) + } + + func recommendations( + forMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await recommendations(forMovie: movieID, page: page, language: language) + } + + func similar( + toMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await similar(toMovie: movieID, page: page, language: language) + } - let linksCollection: MovieExternalLinksCollection - do { - linksCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func nowPlaying( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await nowPlaying(page: page, country: country, language: language) + } + + func popular( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await popular(page: page, country: country, language: language) + } + + func topRated( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await topRated(page: page, country: country, language: language) + } + + func upcoming( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await upcoming(page: page, country: country, language: language) + } - return linksCollection + func watchProviders(forMovie movieID: Movie.ID, country: String = "US") async throws -> ShowWatchProvider? { + try await watchProviders(forMovie: movieID, country: country) } } diff --git a/Sources/TMDb/Domain/Services/Movies/TMDbMovieService.swift b/Sources/TMDb/Domain/Services/Movies/TMDbMovieService.swift new file mode 100644 index 00000000..041306b6 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Movies/TMDbMovieService.swift @@ -0,0 +1,228 @@ +// +// TMDbMovieService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbMovieService: MovieService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details(forMovie id: Movie.ID, language: String? = nil) async throws -> Movie { + let request = MovieRequest(id: id, language: language) + + let movie: Movie + do { + movie = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movie + } + + func credits(forMovie movieID: Movie.ID, language: String? = nil) async throws -> ShowCredits { + let request = MovieCreditsRequest(id: movieID, language: language) + + let credits: ShowCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func reviews( + forMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> ReviewPageableList { + let request = MovieReviewsRequest(id: movieID, page: page, language: language) + + let reviewList: ReviewPageableList + do { + reviewList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return reviewList + } + + func images(forMovie movieID: Movie.ID, filter: MovieImageFilter? = nil) async throws -> ImageCollection { + let request = MovieImagesRequest(id: movieID, languages: filter?.languages) + + let imageCollection: ImageCollection + do { + imageCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return imageCollection + } + + func videos(forMovie movieID: Movie.ID, filter: MovieVideoFilter? = nil) async throws -> VideoCollection { + let request = MovieVideosRequest(id: movieID, languages: filter?.languages) + + let videoCollection: VideoCollection + do { + videoCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return videoCollection + } + + func recommendations( + forMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = MovieRecommendationsRequest(id: movieID, page: page, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func similar( + toMovie movieID: Movie.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = SimilarMoviesRequest(id: movieID, page: page, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func nowPlaying( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = MoviesNowPlayingRequest(page: page, country: country, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func popular( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = PopularMoviesRequest(page: page, country: country, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func topRated( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = TopRatedMoviesRequest(page: page, country: country, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func upcoming( + page: Int? = nil, + country: String? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = UpcomingMoviesRequest(page: page, country: country, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func watchProviders(forMovie movieID: Movie.ID, country: String = "US") async throws -> ShowWatchProvider? { + let request = MovieWatchProvidersRequest(id: movieID) + + let result: ShowWatchProviderResult + do { + result = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return result.results[country] + } + + func externalLinks(forMovie movieID: Movie.ID) async throws -> MovieExternalLinksCollection { + let request = MovieExternalLinksRequest(id: movieID) + + let linksCollection: MovieExternalLinksCollection + do { + linksCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return linksCollection + } + +} diff --git a/Sources/TMDb/Domain/Services/People/PersonService.swift b/Sources/TMDb/Domain/Services/People/PersonService.swift index 03c4b1ca..de30d5c8 100644 --- a/Sources/TMDb/Domain/Services/People/PersonService.swift +++ b/Sources/TMDb/Domain/Services/People/PersonService.swift @@ -23,26 +23,7 @@ import Foundation /// Provides an interface for obtaining people from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class PersonService { - - private static let knownForShowsMaxCount = 10 - - private let apiClient: any APIClient - - /// - /// Creates a person service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol PersonService: Sendable { /// /// Returns the primary information about a person. @@ -57,18 +38,7 @@ public final class PersonService { /// /// - Returns: The matching person. /// - public func details(forPerson id: Person.ID, language: String? = nil) async throws -> Person { - let request = PersonRequest(id: id, language: language) - - let person: Person - do { - person = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return person - } + func details(forPerson id: Person.ID, language: String?) async throws -> Person /// /// Returns the combined movie and TV series credits of a person. @@ -83,21 +53,7 @@ public final class PersonService { /// /// - Returns: The matching person's combined movie and TV series credits. /// - public func combinedCredits( - forPerson personID: Person.ID, - language: String? = nil - ) async throws -> PersonCombinedCredits { - let request = PersonCombinedCreditsRequest(id: personID, language: language) - - let credits: PersonCombinedCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + func combinedCredits(forPerson personID: Person.ID, language: String?) async throws -> PersonCombinedCredits /// /// Returns the movie credits of a person. @@ -112,21 +68,7 @@ public final class PersonService { /// /// - Returns: The matching person's movie credits. /// - public func movieCredits( - forPerson personID: Person.ID, - language: String? = nil - ) async throws -> PersonMovieCredits { - let request = PersonMovieCreditsRequest(id: personID, language: language) - - let credits: PersonMovieCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + func movieCredits(forPerson personID: Person.ID, language: String?) async throws -> PersonMovieCredits /// /// Returns the TV series credits of a person. @@ -141,21 +83,7 @@ public final class PersonService { /// /// - Returns: The matching person's TV series credits. /// - public func tvSeriesCredits( - forPerson personID: Person.ID, - language: String? = nil - ) async throws -> PersonTVSeriesCredits { - let request = PersonTVSeriesCreditsRequest(id: personID, language: language) - - let credits: PersonTVSeriesCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + func tvSeriesCredits(forPerson personID: Person.ID, language: String?) async throws -> PersonTVSeriesCredits /// /// Returns the images for a person. @@ -169,18 +97,7 @@ public final class PersonService { /// /// - Returns: The matching person's images. /// - public func images(forPerson personID: Person.ID) async throws -> PersonImageCollection { - let request = PersonImagesRequest(id: personID) - - let imageCollection: PersonImageCollection - do { - imageCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return imageCollection - } + func images(forPerson personID: Person.ID) async throws -> PersonImageCollection /// /// Returns the list of popular people. @@ -197,18 +114,7 @@ public final class PersonService { /// /// - Returns: Current popular people as a pageable list. /// - public func popular(page: Int? = nil, language: String? = nil) async throws -> PersonPageableList { - let request = PopularPeopleRequest(page: page, language: language) - - let personList: PersonPageableList - do { - personList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return personList - } + func popular(page: Int?, language: String?) async throws -> PersonPageableList /// /// Returns a collection of media databases and social links for a person. @@ -220,17 +126,30 @@ public final class PersonService { /// /// - Returns: A collection of external links for the specificed person. /// - public func externalLinks(forPerson personID: Person.ID) async throws -> PersonExternalLinksCollection { - let request = PersonExternalLinksRequest(id: personID) + func externalLinks(forPerson personID: Person.ID) async throws -> PersonExternalLinksCollection + +} + +public extension PersonService { - let linksCollection: PersonExternalLinksCollection - do { - linksCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func details(forPerson id: Person.ID, language: String? = nil) async throws -> Person { + try await details(forPerson: id, language: language) + } + + func combinedCredits(forPerson personID: Person.ID, language: String? = nil) async throws -> PersonCombinedCredits { + try await combinedCredits(forPerson: personID, language: language) + } + + func movieCredits(forPerson personID: Person.ID, language: String? = nil) async throws -> PersonMovieCredits { + try await movieCredits(forPerson: personID, language: language) + } + + func tvSeriesCredits(forPerson personID: Person.ID, language: String? = nil) async throws -> PersonTVSeriesCredits { + try await tvSeriesCredits(forPerson: personID, language: language) + } - return linksCollection + func popular(page: Int? = nil, language: String? = nil) async throws -> PersonPageableList { + try await popular(page: page, language: language) } } diff --git a/Sources/TMDb/Domain/Services/People/TMDbPersonService.swift b/Sources/TMDb/Domain/Services/People/TMDbPersonService.swift new file mode 100644 index 00000000..f9be0cd7 --- /dev/null +++ b/Sources/TMDb/Domain/Services/People/TMDbPersonService.swift @@ -0,0 +1,131 @@ +// +// TMDbPersonService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbPersonService: PersonService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details(forPerson id: Person.ID, language: String? = nil) async throws -> Person { + let request = PersonRequest(id: id, language: language) + + let person: Person + do { + person = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return person + } + + func combinedCredits( + forPerson personID: Person.ID, + language: String? = nil + ) async throws -> PersonCombinedCredits { + let request = PersonCombinedCreditsRequest(id: personID, language: language) + + let credits: PersonCombinedCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func movieCredits( + forPerson personID: Person.ID, + language: String? = nil + ) async throws -> PersonMovieCredits { + let request = PersonMovieCreditsRequest(id: personID, language: language) + + let credits: PersonMovieCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func tvSeriesCredits( + forPerson personID: Person.ID, + language: String? = nil + ) async throws -> PersonTVSeriesCredits { + let request = PersonTVSeriesCreditsRequest(id: personID, language: language) + + let credits: PersonTVSeriesCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func images(forPerson personID: Person.ID) async throws -> PersonImageCollection { + let request = PersonImagesRequest(id: personID) + + let imageCollection: PersonImageCollection + do { + imageCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return imageCollection + } + + func popular(page: Int? = nil, language: String? = nil) async throws -> PersonPageableList { + let request = PopularPeopleRequest(page: page, language: language) + + let personList: PersonPageableList + do { + personList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return personList + } + + func externalLinks(forPerson personID: Person.ID) async throws -> PersonExternalLinksCollection { + let request = PersonExternalLinksRequest(id: personID) + + let linksCollection: PersonExternalLinksCollection + do { + linksCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return linksCollection + } + +} diff --git a/Sources/TMDb/Domain/Services/Search/AllMediaSearchFilter.swift b/Sources/TMDb/Domain/Services/Search/Filters/AllMediaSearchFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Search/AllMediaSearchFilter.swift rename to Sources/TMDb/Domain/Services/Search/Filters/AllMediaSearchFilter.swift diff --git a/Sources/TMDb/Domain/Services/Search/MovieSearchFilter.swift b/Sources/TMDb/Domain/Services/Search/Filters/MovieSearchFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Search/MovieSearchFilter.swift rename to Sources/TMDb/Domain/Services/Search/Filters/MovieSearchFilter.swift diff --git a/Sources/TMDb/Domain/Services/Search/PersonSearchFilter.swift b/Sources/TMDb/Domain/Services/Search/Filters/PersonSearchFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Search/PersonSearchFilter.swift rename to Sources/TMDb/Domain/Services/Search/Filters/PersonSearchFilter.swift diff --git a/Sources/TMDb/Domain/Services/Search/TVSeriesSearchFilter.swift b/Sources/TMDb/Domain/Services/Search/Filters/TVSeriesSearchFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Search/TVSeriesSearchFilter.swift rename to Sources/TMDb/Domain/Services/Search/Filters/TVSeriesSearchFilter.swift diff --git a/Sources/TMDb/Domain/Services/Search/SearchService.swift b/Sources/TMDb/Domain/Services/Search/SearchService.swift index 3badde2f..2d33fa43 100644 --- a/Sources/TMDb/Domain/Services/Search/SearchService.swift +++ b/Sources/TMDb/Domain/Services/Search/SearchService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for searching content from TMDb.. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class SearchService { - - private let apiClient: any APIClient - - /// - /// Creates a search service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol SearchService: Sendable { /// /// Returns search results for movies, TV series and people based on a query. @@ -59,28 +42,12 @@ public final class SearchService { /// /// - Returns: Movies, TV series and people matching the query. /// - public func searchAll( + func searchAll( query: String, - filter: AllMediaSearchFilter? = nil, - page: Int? = nil, - language: String? = nil - ) async throws -> MediaPageableList { - let request = MultiSearchRequest( - query: query, - includeAdult: filter?.includeAdult, - page: page, - language: language - ) - - let mediaList: MediaPageableList - do { - mediaList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return mediaList - } + filter: AllMediaSearchFilter?, + page: Int?, + language: String? + ) async throws -> MediaPageableList /// /// Returns search results for movies. @@ -99,30 +66,12 @@ public final class SearchService { /// /// - Returns: Movies matching the query. /// - public func searchMovies( + func searchMovies( query: String, - filter: MovieSearchFilter? = nil, - page: Int? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = MovieSearchRequest( - query: query, - primaryReleaseYear: filter?.primaryReleaseYear, - country: filter?.country, - includeAdult: filter?.includeAdult, - page: page, - language: language - ) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + filter: MovieSearchFilter?, + page: Int?, + language: String? + ) async throws -> MoviePageableList /// /// Returns search results for TV series. @@ -141,30 +90,12 @@ public final class SearchService { /// /// - Returns: TV series matching the query. /// - public func searchTVSeries( + func searchTVSeries( query: String, - filter: TVSeriesSearchFilter? = nil, - page: Int? = nil, - language: String? = nil - ) async throws -> TVSeriesPageableList { - let request = TVSeriesSearchRequest( - query: query, - firstAirDateYear: filter?.firstAirDateYear, - year: filter?.year, - includeAdult: filter?.includeAdult, - page: page, - language: language - ) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + filter: TVSeriesSearchFilter?, + page: Int?, + language: String? + ) async throws -> TVSeriesPageableList /// /// Returns search results for people. @@ -183,27 +114,51 @@ public final class SearchService { /// /// - Returns: People matching the query. /// - public func searchPeople( + func searchPeople( query: String, - filter: PersonSearchFilter? = nil, + filter: PersonSearchFilter?, + page: Int?, + language: String? + ) async throws -> PersonPageableList + +} + +public extension SearchService { + + func searchAll( + query: String, + filter: AllMediaSearchFilter? = nil, page: Int? = nil, language: String? = nil - ) async throws -> PersonPageableList { - let request = PersonSearchRequest( - query: query, - includeAdult: filter?.includeAdult, - page: page, - language: language - ) + ) async throws -> MediaPageableList { + try await searchAll(query: query, filter: filter, page: page, language: language) + } + + func searchMovies( + query: String, + filter: MovieSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + try await searchMovies(query: query, filter: filter, page: page, language: language) + } - let peopleList: PersonPageableList - do { - peopleList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func searchTVSeries( + query: String, + filter: TVSeriesSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + try await searchTVSeries(query: query, filter: filter, page: page, language: language) + } - return peopleList + func searchPeople( + query: String, + filter: PersonSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> PersonPageableList { + try await searchPeople(query: query, filter: filter, page: page, language: language) } } diff --git a/Sources/TMDb/Domain/Services/Search/TMDbSearchService.swift b/Sources/TMDb/Domain/Services/Search/TMDbSearchService.swift new file mode 100644 index 00000000..17fc81b7 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Search/TMDbSearchService.swift @@ -0,0 +1,127 @@ +// +// TMDbSearchService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbSearchService: SearchService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func searchAll( + query: String, + filter: AllMediaSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> MediaPageableList { + let request = MultiSearchRequest( + query: query, + includeAdult: filter?.includeAdult, + page: page, + language: language + ) + + let mediaList: MediaPageableList + do { + mediaList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return mediaList + } + + func searchMovies( + query: String, + filter: MovieSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = MovieSearchRequest( + query: query, + primaryReleaseYear: filter?.primaryReleaseYear, + country: filter?.country, + includeAdult: filter?.includeAdult, + page: page, + language: language + ) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func searchTVSeries( + query: String, + filter: TVSeriesSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + let request = TVSeriesSearchRequest( + query: query, + firstAirDateYear: filter?.firstAirDateYear, + year: filter?.year, + includeAdult: filter?.includeAdult, + page: page, + language: language + ) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func searchPeople( + query: String, + filter: PersonSearchFilter? = nil, + page: Int? = nil, + language: String? = nil + ) async throws -> PersonPageableList { + let request = PersonSearchRequest( + query: query, + includeAdult: filter?.includeAdult, + page: page, + language: language + ) + + let peopleList: PersonPageableList + do { + peopleList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return peopleList + } + +} diff --git a/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeImageFilter.swift b/Sources/TMDb/Domain/Services/TVEpisodes/Filters/TVEpisodeImageFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeImageFilter.swift rename to Sources/TMDb/Domain/Services/TVEpisodes/Filters/TVEpisodeImageFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeVideoFilter.swift b/Sources/TMDb/Domain/Services/TVEpisodes/Filters/TVEpisodeVideoFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeVideoFilter.swift rename to Sources/TMDb/Domain/Services/TVEpisodes/Filters/TVEpisodeVideoFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVEpisodes/TMDbTVEpisodeService.swift b/Sources/TMDb/Domain/Services/TVEpisodes/TMDbTVEpisodeService.swift new file mode 100644 index 00000000..f6eca5d1 --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVEpisodes/TMDbTVEpisodeService.swift @@ -0,0 +1,100 @@ +// +// TMDbTVEpisodeService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbTVEpisodeService: TVEpisodeService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVEpisode { + let request = TVEpisodeRequest( + episodeNumber: episodeNumber, + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + language: language + ) + + let episode: TVEpisode + do { + episode = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return episode + } + + func images( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVEpisodeImageFilter? = nil + ) async throws -> TVEpisodeImageCollection { + let request = TVEpisodeImagesRequest( + episodeNumber: episodeNumber, + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + languages: filter?.languages + ) + + let imageCollection: TVEpisodeImageCollection + do { + imageCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return imageCollection + } + + func videos( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVEpisodeVideoFilter? = nil + ) async throws -> VideoCollection { + let request = TVEpisodeVideosRequest( + episodeNumber: episodeNumber, + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + languages: filter?.languages + ) + + let videoCollection: VideoCollection + do { + videoCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return videoCollection + } + +} diff --git a/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeService.swift b/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeService.swift index effcb3d3..d9526b90 100644 --- a/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeService.swift +++ b/Sources/TMDb/Domain/Services/TVEpisodes/TVEpisodeService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining TV episodes from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class TVEpisodeService { - - private let apiClient: any APIClient - - /// - /// Creates a TV episode service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol TVEpisodeService: Sendable { /// /// Returns the primary information about a TV episode. @@ -57,28 +40,12 @@ public final class TVEpisodeService { /// /// - Returns: A episode of the matching TV series. /// - public func details( + func details( forEpisode episodeNumber: Int, inSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - language: String? = nil - ) async throws -> TVEpisode { - let request = TVEpisodeRequest( - episodeNumber: episodeNumber, - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - language: language - ) - - let episode: TVEpisode - do { - episode = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return episode - } + language: String? + ) async throws -> TVEpisode /// /// Returns the images that belong to a TV episode. @@ -95,28 +62,12 @@ public final class TVEpisodeService { /// /// - Returns: A collection of images for the matching TV's episode. /// - public func images( + func images( forEpisode episodeNumber: Int, inSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - filter: TVEpisodeImageFilter? = nil - ) async throws -> TVEpisodeImageCollection { - let request = TVEpisodeImagesRequest( - episodeNumber: episodeNumber, - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - languages: filter?.languages - ) - - let imageCollection: TVEpisodeImageCollection - do { - imageCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return imageCollection - } + filter: TVEpisodeImageFilter? + ) async throws -> TVEpisodeImageCollection /// /// Returns the videos that belong to a TV series episode. @@ -133,27 +84,57 @@ public final class TVEpisodeService { /// /// - Returns: A collection of videos for the matching TV's episode. /// - public func videos( + func videos( forEpisode episodeNumber: Int, inSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - filter: TVEpisodeVideoFilter? = nil - ) async throws -> VideoCollection { - let request = TVEpisodeVideosRequest( - episodeNumber: episodeNumber, - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - languages: filter?.languages + filter: TVEpisodeVideoFilter? + ) async throws -> VideoCollection + +} + +public extension TVEpisodeService { + + func details( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVEpisode { + try await details( + forEpisode: episodeNumber, + inSeason: seasonNumber, + inTVSeries: tvSeriesID, + language: language ) + } - let videoCollection: VideoCollection - do { - videoCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func images( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVEpisodeImageFilter? = nil + ) async throws -> TVEpisodeImageCollection { + try await images( + forEpisode: episodeNumber, + inSeason: seasonNumber, + inTVSeries: tvSeriesID, + filter: filter + ) + } - return videoCollection + func videos( + forEpisode episodeNumber: Int, + inSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVEpisodeVideoFilter? = nil + ) async throws -> VideoCollection { + try await videos( + forEpisode: episodeNumber, + inSeason: seasonNumber, + inTVSeries: tvSeriesID, + filter: filter + ) } } diff --git a/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonImageFilter.swift b/Sources/TMDb/Domain/Services/TVSeasons/Filters/TVSeasonImageFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVSeasons/TVSeasonImageFilter.swift rename to Sources/TMDb/Domain/Services/TVSeasons/Filters/TVSeasonImageFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonVideoFilter.swift b/Sources/TMDb/Domain/Services/TVSeasons/Filters/TVSeasonVideoFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVSeasons/TVSeasonVideoFilter.swift rename to Sources/TMDb/Domain/Services/TVSeasons/Filters/TVSeasonVideoFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVSeasons/TMDbTVSeasonService.swift b/Sources/TMDb/Domain/Services/TVSeasons/TMDbTVSeasonService.swift new file mode 100644 index 00000000..1c8d290a --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVSeasons/TMDbTVSeasonService.swift @@ -0,0 +1,115 @@ +// +// TMDbTVSeasonService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbTVSeasonService: TVSeasonService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeason { + let request = TVSeasonRequest( + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + language: language + ) + + let season: TVSeason + do { + season = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return season + } + + func aggregateCredits( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeasonAggregateCredits { + let request = TVSeasonAggregateCreditsRequest( + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + language: language + ) + + let credits: TVSeasonAggregateCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func images( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeasonImageFilter? = nil + ) async throws -> TVSeasonImageCollection { + let request = TVSeasonImagesRequest( + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + languages: filter?.languages + ) + + let imageCollection: TVSeasonImageCollection + do { + imageCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return imageCollection + } + + func videos( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeasonVideoFilter? = nil + ) async throws -> VideoCollection { + let request = TVSeasonVideosRequest( + seasonNumber: seasonNumber, + tvSeriesID: tvSeriesID, + languages: filter?.languages + ) + + let videoCollection: VideoCollection + do { + videoCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return videoCollection + } + +} diff --git a/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonService.swift b/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonService.swift index f874a5a3..2f3d28fa 100644 --- a/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonService.swift +++ b/Sources/TMDb/Domain/Services/TVSeasons/TVSeasonService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining TV seasons from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class TVSeasonService { - - private let apiClient: any APIClient - - /// - /// Creates a TV season service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol TVSeasonService: Sendable { /// /// Returns the primary information about a TV season. @@ -56,26 +39,11 @@ public final class TVSeasonService { /// /// - Returns: A season of the matching TV series. /// - public func details( + func details( forSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - language: String? = nil - ) async throws -> TVSeason { - let request = TVSeasonRequest( - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - language: language - ) - - let season: TVSeason - do { - season = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return season - } + language: String? + ) async throws -> TVSeason /// /// Returns the aggregate cast and crew of a TV season. @@ -95,26 +63,11 @@ public final class TVSeasonService { /// /// - Returns: Show credits for the matching TV season. /// - public func aggregateCredits( + func aggregateCredits( forSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - language: String? = nil - ) async throws -> TVSeasonAggregateCredits { - let request = TVSeasonAggregateCreditsRequest( - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - language: language - ) - - let credits: TVSeasonAggregateCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + language: String? + ) async throws -> TVSeasonAggregateCredits /// /// Returns the images that belong to a TV season. @@ -130,26 +83,11 @@ public final class TVSeasonService { /// /// - Returns: A collection of images for the matching TV's season. /// - public func images( + func images( forSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - filter: TVSeasonImageFilter? = nil - ) async throws -> TVSeasonImageCollection { - let request = TVSeasonImagesRequest( - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - languages: filter?.languages - ) - - let imageCollection: TVSeasonImageCollection - do { - imageCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return imageCollection - } + filter: TVSeasonImageFilter? + ) async throws -> TVSeasonImageCollection /// /// Returns the videos that belong to a TV season. @@ -165,25 +103,46 @@ public final class TVSeasonService { /// /// - Returns: A collection of videos for the matching TV series season. /// - public func videos( + func videos( forSeason seasonNumber: Int, inTVSeries tvSeriesID: TVSeries.ID, - filter: TVSeasonVideoFilter? = nil - ) async throws -> VideoCollection { - let request = TVSeasonVideosRequest( - seasonNumber: seasonNumber, - tvSeriesID: tvSeriesID, - languages: filter?.languages - ) + filter: TVSeasonVideoFilter? + ) async throws -> VideoCollection - let videoCollection: VideoCollection - do { - videoCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } +} + +public extension TVSeasonService { + + func details( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeason { + try await details(forSeason: seasonNumber, inTVSeries: tvSeriesID, language: language) + } + + func aggregateCredits( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeasonAggregateCredits { + try await aggregateCredits(forSeason: seasonNumber, inTVSeries: tvSeriesID, language: language) + } + + func images( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeasonImageFilter? = nil + ) async throws -> TVSeasonImageCollection { + try await images(forSeason: seasonNumber, inTVSeries: tvSeriesID, filter: filter) + } - return videoCollection + func videos( + forSeason seasonNumber: Int, + inTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeasonVideoFilter? = nil + ) async throws -> VideoCollection { + try await videos(forSeason: seasonNumber, inTVSeries: tvSeriesID, filter: filter) } } diff --git a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesImageFilter.swift b/Sources/TMDb/Domain/Services/TVSeries/Filters/TVSeriesImageFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVSeries/TVSeriesImageFilter.swift rename to Sources/TMDb/Domain/Services/TVSeries/Filters/TVSeriesImageFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesVideoFilter.swift b/Sources/TMDb/Domain/Services/TVSeries/Filters/TVSeriesVideoFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/TVSeries/TVSeriesVideoFilter.swift rename to Sources/TMDb/Domain/Services/TVSeries/Filters/TVSeriesVideoFilter.swift diff --git a/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift b/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift new file mode 100644 index 00000000..c6fe1ecf --- /dev/null +++ b/Sources/TMDb/Domain/Services/TVSeries/TMDbTVSeriesService.swift @@ -0,0 +1,198 @@ +// +// TMDbTVSeriesService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbTVSeriesService: TVSeriesService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func details(forTVSeries id: TVSeries.ID, language: String? = nil) async throws -> TVSeries { + let request = TVSeriesRequest(id: id, language: language) + + let tvSeries: TVSeries + do { + tvSeries = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeries + } + + func credits(forTVSeries tvSeriesID: TVSeries.ID, language: String? = nil) async throws -> ShowCredits { + let request = TVSeriesCreditsRequest(id: tvSeriesID, language: language) + + let credits: ShowCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func aggregateCredits( + forTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeriesAggregateCredits { + let request = TVSeriesAggregateCreditsRequest(id: tvSeriesID, language: language) + + let credits: TVSeriesAggregateCredits + do { + credits = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return credits + } + + func reviews( + forTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> ReviewPageableList { + let request = TVSeriesReviewsRequest(id: tvSeriesID, page: page, language: language) + + let reviewList: ReviewPageableList + do { + reviewList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return reviewList + } + + func images( + forTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeriesImageFilter? = nil + ) async throws -> ImageCollection { + let request = TVSeriesImagesRequest(id: tvSeriesID, languages: filter?.languages) + + let imageCollection: ImageCollection + do { + imageCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return imageCollection + } + + func videos( + forTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeriesVideoFilter? = nil + ) async throws -> VideoCollection { + let request = TVSeriesVideosRequest(id: tvSeriesID, languages: filter?.languages) + + let videoCollection: VideoCollection + do { + videoCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return videoCollection + } + + func recommendations( + forTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + let request = TVSeriesRecommendationsRequest(id: tvSeriesID, page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func similar( + toTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + let request = SimilarTVSeriesRequest(id: tvSeriesID, page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func popular(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { + let request = PopularTVSeriesRequest(page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func watchProviders( + forTVSeries tvSeriesID: TVSeries.ID, + country: String = "US" + ) async throws -> ShowWatchProvider? { + let request = TVSeriesWatchProvidersRequest(id: tvSeriesID) + + let result: ShowWatchProviderResult + do { + result = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return result.results[country] + } + + func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection { + let request = TVSeriesExternalLinksRequest(id: tvSeriesID) + + let linksCollection: TVSeriesExternalLinksCollection + do { + linksCollection = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return linksCollection + } + +} diff --git a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift b/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift index b3dd97cc..67e65852 100644 --- a/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift +++ b/Sources/TMDb/Domain/Services/TVSeries/TVSeriesService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining TV series from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class TVSeriesService { - - private let apiClient: any APIClient - - /// - /// Creates a TV series service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol TVSeriesService: Sendable { /// /// Returns the primary information about a TV series. @@ -55,18 +38,7 @@ public final class TVSeriesService { /// /// - Returns: The matching TV series. /// - public func details(forTVSeries id: TVSeries.ID, language: String? = nil) async throws -> TVSeries { - let request = TVSeriesRequest(id: id, language: language) - - let tvSeries: TVSeries - do { - tvSeries = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeries - } + func details(forTVSeries id: TVSeries.ID, language: String?) async throws -> TVSeries /// /// Returns the cast and crew of a TV series. @@ -81,18 +53,7 @@ public final class TVSeriesService { /// /// - Returns: Show credits for the matching TV series. /// - public func credits(forTVSeries tvSeriesID: TVSeries.ID, language: String? = nil) async throws -> ShowCredits { - let request = TVSeriesCreditsRequest(id: tvSeriesID, language: language) - - let credits: ShowCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + func credits(forTVSeries tvSeriesID: TVSeries.ID, language: String?) async throws -> ShowCredits /// /// Returns the aggregate cast and crew of a TV series. @@ -111,21 +72,10 @@ public final class TVSeriesService { /// /// - Returns: Show credits for the matching TV series. /// - public func aggregateCredits( + func aggregateCredits( forTVSeries tvSeriesID: TVSeries.ID, - language: String? = nil - ) async throws -> TVSeriesAggregateCredits { - let request = TVSeriesAggregateCreditsRequest(id: tvSeriesID, language: language) - - let credits: TVSeriesAggregateCredits - do { - credits = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return credits - } + language: String? + ) async throws -> TVSeriesAggregateCredits /// /// Returns the user reviews for a TV series. @@ -143,22 +93,7 @@ public final class TVSeriesService { /// /// - Returns: Reviews for the matching TV series as a pageable list. /// - public func reviews( - forTVSeries tvSeriesID: TVSeries.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> ReviewPageableList { - let request = TVSeriesReviewsRequest(id: tvSeriesID, page: page, language: language) - - let reviewList: ReviewPageableList - do { - reviewList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return reviewList - } + func reviews(forTVSeries tvSeriesID: TVSeries.ID, page: Int?, language: String?) async throws -> ReviewPageableList /// /// Returns the images that belong to a TV series. @@ -173,21 +108,7 @@ public final class TVSeriesService { /// /// - Returns: A collection of images for the matching TV series. /// - public func images( - forTVSeries tvSeriesID: TVSeries.ID, - filter: TVSeriesImageFilter? = nil - ) async throws -> ImageCollection { - let request = TVSeriesImagesRequest(id: tvSeriesID, languages: filter?.languages) - - let imageCollection: ImageCollection - do { - imageCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return imageCollection - } + func images(forTVSeries tvSeriesID: TVSeries.ID, filter: TVSeriesImageFilter?) async throws -> ImageCollection /// /// Returns the videos that belong to a TV series. @@ -202,21 +123,7 @@ public final class TVSeriesService { /// /// - Returns: A collection of videos for the matching TV series. /// - public func videos( - forTVSeries tvSeriesID: TVSeries.ID, - filter: TVSeriesVideoFilter? = nil - ) async throws -> VideoCollection { - let request = TVSeriesVideosRequest(id: tvSeriesID, languages: filter?.languages) - - let videoCollection: VideoCollection - do { - videoCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return videoCollection - } + func videos(forTVSeries tvSeriesID: TVSeries.ID, filter: TVSeriesVideoFilter?) async throws -> VideoCollection /// /// Returns a list of recommended TV series for a TV series. @@ -234,22 +141,11 @@ public final class TVSeriesService { /// /// - Returns: Recommended TV series for the matching TV series as a pageable list. /// - public func recommendations( + func recommendations( forTVSeries tvSeriesID: TVSeries.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> TVSeriesPageableList { - let request = TVSeriesRecommendationsRequest(id: tvSeriesID, page: page, language: language) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + page: Int?, + language: String? + ) async throws -> TVSeriesPageableList /// /// Returns a list of similar TV series for a TV series. @@ -269,22 +165,11 @@ public final class TVSeriesService { /// /// - Returns: Similar TV series for the matching TV series as a pageable list. /// - public func similar( + func similar( toTVSeries tvSeriesID: TVSeries.ID, - page: Int? = nil, - language: String? = nil - ) async throws -> TVSeriesPageableList { - let request = SimilarTVSeriesRequest(id: tvSeriesID, page: page, language: language) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + page: Int?, + language: String? + ) async throws -> TVSeriesPageableList /// /// Returns a list current popular TV series. @@ -301,18 +186,7 @@ public final class TVSeriesService { /// /// - Returns: Current popular TV series as a pageable list. /// - public func popular(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { - let request = PopularTVSeriesRequest(page: page, language: language) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + func popular(page: Int?, language: String?) async throws -> TVSeriesPageableList /// /// Returns watch providers for a TV series @@ -329,21 +203,7 @@ public final class TVSeriesService { /// /// - Returns: Watch providers for TV series in current region. /// - public func watchProviders( - forTVSeries tvSeriesID: TVSeries.ID, - country: String = "US" - ) async throws -> ShowWatchProvider? { - let request = TVSeriesWatchProvidersRequest(id: tvSeriesID) - - let result: ShowWatchProviderResult - do { - result = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return result.results[country] - } + func watchProviders(forTVSeries tvSeriesID: TVSeries.ID, country: String) async throws -> ShowWatchProvider? /// /// Returns a collection of media databases and social links for a TV series. @@ -355,17 +215,74 @@ public final class TVSeriesService { /// /// - Returns: A collection of external links for the specificed TV series. /// - public func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection { - let request = TVSeriesExternalLinksRequest(id: tvSeriesID) + func externalLinks(forTVSeries tvSeriesID: TVSeries.ID) async throws -> TVSeriesExternalLinksCollection + +} + +public extension TVSeriesService { + + func details(forTVSeries id: TVSeries.ID, language: String? = nil) async throws -> TVSeries { + try await details(forTVSeries: id, language: language) + } + + func credits(forTVSeries tvSeriesID: TVSeries.ID, language: String? = nil) async throws -> ShowCredits { + try await credits(forTVSeries: tvSeriesID, language: language) + } - let linksCollection: TVSeriesExternalLinksCollection - do { - linksCollection = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func aggregateCredits( + forTVSeries tvSeriesID: TVSeries.ID, + language: String? = nil + ) async throws -> TVSeriesAggregateCredits { + try await aggregateCredits(forTVSeries: tvSeriesID, language: language) + } - return linksCollection + func reviews( + forTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> ReviewPageableList { + try await reviews(forTVSeries: tvSeriesID, page: page, language: language) + } + + func images( + forTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeriesImageFilter? = nil + ) async throws -> ImageCollection { + try await images(forTVSeries: tvSeriesID, filter: filter) + } + + func videos( + forTVSeries tvSeriesID: TVSeries.ID, + filter: TVSeriesVideoFilter? = nil + ) async throws -> VideoCollection { + try await videos(forTVSeries: tvSeriesID, filter: filter) + } + + func recommendations( + forTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + try await recommendations(forTVSeries: tvSeriesID, page: page, language: language) + } + + func similar( + toTVSeries tvSeriesID: TVSeries.ID, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + try await similar(toTVSeries: tvSeriesID, page: page, language: language) + } + + func popular(page: Int? = nil, language: String? = nil) async throws -> TVSeriesPageableList { + try await popular(page: page, language: language) + } + + func watchProviders( + forTVSeries tvSeriesID: TVSeries.ID, + country: String = "US" + ) async throws -> ShowWatchProvider? { + try await watchProviders(forTVSeries: tvSeriesID, country: country) } } diff --git a/Sources/TMDb/Domain/Services/Trending/TrendingTimeWindowFilterType.swift b/Sources/TMDb/Domain/Services/Trending/Filters/TrendingTimeWindowFilterType.swift similarity index 100% rename from Sources/TMDb/Domain/Services/Trending/TrendingTimeWindowFilterType.swift rename to Sources/TMDb/Domain/Services/Trending/Filters/TrendingTimeWindowFilterType.swift diff --git a/Sources/TMDb/Domain/Services/Trending/TMDbTrendingService.swift b/Sources/TMDb/Domain/Services/Trending/TMDbTrendingService.swift new file mode 100644 index 00000000..e0096883 --- /dev/null +++ b/Sources/TMDb/Domain/Services/Trending/TMDbTrendingService.swift @@ -0,0 +1,82 @@ +// +// TMDbTrendingService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbTrendingService: TrendingService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func movies( + inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, + page: Int? = nil, + language: String? = nil + ) async throws -> MoviePageableList { + let request = TrendingMoviesRequest(timeWindow: timeWindow, page: page, language: language) + + let movieList: MoviePageableList + do { + movieList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return movieList + } + + func tvSeries( + inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + let request = TrendingTVSeriesRequest(timeWindow: timeWindow, page: page, language: language) + + let tvSeriesList: TVSeriesPageableList + do { + tvSeriesList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return tvSeriesList + } + + func people( + inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, + page: Int? = nil, + language: String? = nil + ) async throws -> PersonPageableList { + let request = TrendingPeopleRequest(timeWindow: timeWindow, page: page, language: language) + + let peopleList: PersonPageableList + do { + peopleList = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return peopleList + } + +} diff --git a/Sources/TMDb/Domain/Services/Trending/TrendingService.swift b/Sources/TMDb/Domain/Services/Trending/TrendingService.swift index f2799f2a..afbd9f4a 100644 --- a/Sources/TMDb/Domain/Services/Trending/TrendingService.swift +++ b/Sources/TMDb/Domain/Services/Trending/TrendingService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for finding trending movies, TV series and people from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class TrendingService { - - private let apiClient: any APIClient - - /// - /// Creates a trending service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol TrendingService: Sendable { /// /// Returns a list of the daily or weekly trending movies. @@ -61,22 +44,11 @@ public final class TrendingService { /// /// - Returns: Trending movies in a time window as a pageable list. /// - public func movies( - inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, - page: Int? = nil, - language: String? = nil - ) async throws -> MoviePageableList { - let request = TrendingMoviesRequest(timeWindow: timeWindow, page: page, language: language) - - let movieList: MoviePageableList - do { - movieList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return movieList - } + func movies( + inTimeWindow timeWindow: TrendingTimeWindowFilterType, + page: Int?, + language: String? + ) async throws -> MoviePageableList /// /// Returns a list of the daily or weekly trending TV series. @@ -97,22 +69,11 @@ public final class TrendingService { /// /// - Returns: Trending TV series in a time window as a pageable list. /// - public func tvSeries( - inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, - page: Int? = nil, - language: String? = nil - ) async throws -> TVSeriesPageableList { - let request = TrendingTVSeriesRequest(timeWindow: timeWindow, page: page, language: language) - - let tvSeriesList: TVSeriesPageableList - do { - tvSeriesList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return tvSeriesList - } + func tvSeries( + inTimeWindow timeWindow: TrendingTimeWindowFilterType, + page: Int?, + language: String? + ) async throws -> TVSeriesPageableList /// /// Returns a list of the daily or weekly trending people. @@ -133,21 +94,38 @@ public final class TrendingService { /// /// - Returns: Trending people in a time window as a pageable list. /// - public func people( + func people( + inTimeWindow timeWindow: TrendingTimeWindowFilterType, + page: Int?, + language: String? + ) async throws -> PersonPageableList + +} + +public extension TrendingService { + + func movies( inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, page: Int? = nil, language: String? = nil - ) async throws -> PersonPageableList { - let request = TrendingPeopleRequest(timeWindow: timeWindow, page: page, language: language) + ) async throws -> MoviePageableList { + try await movies(inTimeWindow: timeWindow, page: page, language: language) + } - let peopleList: PersonPageableList - do { - peopleList = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + func tvSeries( + inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, + page: Int? = nil, + language: String? = nil + ) async throws -> TVSeriesPageableList { + try await tvSeries(inTimeWindow: timeWindow, page: page, language: language) + } - return peopleList + func people( + inTimeWindow timeWindow: TrendingTimeWindowFilterType = .day, + page: Int? = nil, + language: String? = nil + ) async throws -> PersonPageableList { + try await people(inTimeWindow: timeWindow, page: page, language: language) } } diff --git a/Sources/TMDb/Domain/Services/WatchProviders/WatchProviderFilter.swift b/Sources/TMDb/Domain/Services/WatchProviders/Filters/WatchProviderFilter.swift similarity index 100% rename from Sources/TMDb/Domain/Services/WatchProviders/WatchProviderFilter.swift rename to Sources/TMDb/Domain/Services/WatchProviders/Filters/WatchProviderFilter.swift diff --git a/Sources/TMDb/Domain/Services/WatchProviders/TMDbWatchProviderService.swift b/Sources/TMDb/Domain/Services/WatchProviders/TMDbWatchProviderService.swift new file mode 100644 index 00000000..4f515254 --- /dev/null +++ b/Sources/TMDb/Domain/Services/WatchProviders/TMDbWatchProviderService.swift @@ -0,0 +1,76 @@ +// +// TMDbWatchProviderService.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) +final class TMDbWatchProviderService: WatchProviderService { + + private let apiClient: any APIClient + + init(apiClient: some APIClient) { + self.apiClient = apiClient + } + + func countries(language: String? = nil) async throws -> [Country] { + let request = WatchProviderRegionsRequest(language: language) + + let regions: WatchProviderRegions + do { + regions = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return regions.results + } + + func movieWatchProviders( + filter: WatchProviderFilter? = nil, + language: String? = nil + ) async throws -> [WatchProvider] { + let request = WatchProvidersForMoviesRequest(country: filter?.country, language: language) + + let result: WatchProviderResult + do { + result = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return result.results + } + + func tvSeriesWatchProviders( + filter: WatchProviderFilter? = nil, + language: String? = nil + ) async throws -> [WatchProvider] { + let request = WatchProvidersForTVSeriesRequest(country: filter?.country, language: language) + + let result: WatchProviderResult + do { + result = try await apiClient.perform(request) + } catch let error { + throw TMDbError(error: error) + } + + return result.results + } + +} diff --git a/Sources/TMDb/Domain/Services/WatchProviders/WatchProviderService.swift b/Sources/TMDb/Domain/Services/WatchProviders/WatchProviderService.swift index 35127a77..af3a1f51 100644 --- a/Sources/TMDb/Domain/Services/WatchProviders/WatchProviderService.swift +++ b/Sources/TMDb/Domain/Services/WatchProviders/WatchProviderService.swift @@ -23,24 +23,7 @@ import Foundation /// Provides an interface for obtaining watch providers from TMDb. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -public final class WatchProviderService { - - private let apiClient: any APIClient - - /// - /// Creates a watch provider service object. - /// - /// - Parameter configuration: A TMDb configuration object. - /// - public convenience init(configuration: some ConfigurationProviding) { - self.init( - apiClient: TMDbFactory.apiClient(configuration: configuration) - ) - } - - init(apiClient: some APIClient) { - self.apiClient = apiClient - } +public protocol WatchProviderService: Sendable { /// /// Returns a list of all of the countries TMDb have watch provider (OTT/streaming) data for. @@ -54,18 +37,7 @@ public final class WatchProviderService { /// /// - Returns: Countries TMDb have watch provider data for. /// - public func countries(language: String? = nil) async throws -> [Country] { - let request = WatchProviderRegionsRequest(language: language) - - let regions: WatchProviderRegions - do { - regions = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return regions.results - } + func countries(language: String?) async throws -> [Country] /// /// Returns a list of the watch provider (OTT/streaming) data TMDb have available for movies. @@ -80,21 +52,7 @@ public final class WatchProviderService { /// /// - Returns: Watch providers for movies. /// - public func movieWatchProviders( - filter: WatchProviderFilter? = nil, - language: String? = nil - ) async throws -> [WatchProvider] { - let request = WatchProvidersForMoviesRequest(country: filter?.country, language: language) - - let result: WatchProviderResult - do { - result = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } - - return result.results - } + func movieWatchProviders(filter: WatchProviderFilter?, language: String?) async throws -> [WatchProvider] /// /// Returns a list of the watch provider (OTT/streaming) data TMDb have available for TV series. @@ -109,20 +67,28 @@ public final class WatchProviderService { /// /// - Returns: Watch providers for TV series. /// - public func tvSeriesWatchProviders( + func tvSeriesWatchProviders(filter: WatchProviderFilter?, language: String?) async throws -> [WatchProvider] + +} + +public extension WatchProviderService { + + func countries(language: String? = nil) async throws -> [Country] { + try await countries(language: language) + } + + func movieWatchProviders( filter: WatchProviderFilter? = nil, language: String? = nil ) async throws -> [WatchProvider] { - let request = WatchProvidersForTVSeriesRequest(country: filter?.country, language: language) - - let result: WatchProviderResult - do { - result = try await apiClient.perform(request) - } catch let error { - throw TMDbError(error: error) - } + try await movieWatchProviders(filter: filter, language: language) + } - return result.results + func tvSeriesWatchProviders( + filter: WatchProviderFilter? = nil, + language: String? = nil + ) async throws -> [WatchProvider] { + try await tvSeriesWatchProviders(filter: filter, language: language) } } diff --git a/Sources/TMDb/Networking/HTTPClient/HTTPRequest.swift b/Sources/TMDb/Networking/HTTPClient/HTTPRequest.swift index 941a4994..438086d8 100644 --- a/Sources/TMDb/Networking/HTTPClient/HTTPRequest.swift +++ b/Sources/TMDb/Networking/HTTPClient/HTTPRequest.swift @@ -19,13 +19,39 @@ import Foundation +/// +/// A model representing an HTTP request. +/// public struct HTTPRequest: Sendable { + /// + /// Request's URL. + /// public let url: URL + + /// + /// HTTP method. + /// public let method: HTTPRequest.Method + + /// + /// HTTP headers. + /// public let headers: [String: String] + + /// + /// Body data. + /// public let body: Data? + /// + /// Create an HTTP request object. + /// + /// - Parameters: + /// - url: Request's URL. + /// - method: HTTP method. + /// - headers: HTTP headers. + /// - body: Body data. public init( url: URL, method: HTTPRequest.Method = .get, @@ -42,10 +68,26 @@ public struct HTTPRequest: Sendable { public extension HTTPRequest { + /// + /// An enumeration representing HTTP methods. + /// enum Method: String, Sendable { + + /// + /// HTTP GET method. + /// case get = "GET" + + /// + /// HTTP POST method. + /// case post = "POST" + + /// + /// HTTP DELETE method. + /// case delete = "DELETE" + } } diff --git a/Sources/TMDb/Networking/Serialiser.swift b/Sources/TMDb/Networking/Serialiser.swift index d2c31f37..fb4c9f9b 100644 --- a/Sources/TMDb/Networking/Serialiser.swift +++ b/Sources/TMDb/Networking/Serialiser.swift @@ -19,7 +19,7 @@ import Foundation -protocol Serialiser { +protocol Serialiser: Sendable { var mimeType: String { get } diff --git a/Sources/TMDb/Networking/TMDbAPIClient.swift b/Sources/TMDb/Networking/TMDbAPIClient.swift index cfe81e45..bda2ca15 100644 --- a/Sources/TMDb/Networking/TMDbAPIClient.swift +++ b/Sources/TMDb/Networking/TMDbAPIClient.swift @@ -19,7 +19,7 @@ import Foundation -final class TMDbAPIClient: APIClient, @unchecked Sendable { +final class TMDbAPIClient: APIClient, Sendable { private let apiKey: String private let baseURL: URL diff --git a/Sources/TMDb/TMDBClient.swift b/Sources/TMDb/TMDBClient.swift new file mode 100644 index 00000000..ca68f0f7 --- /dev/null +++ b/Sources/TMDb/TMDBClient.swift @@ -0,0 +1,183 @@ +// +// TMDBClient.swift +// TMDb +// +// Copyright © 2024 Adam Young. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// +/// The TMDb client. +/// +public final class TMDbClient: Sendable { + + /// + /// TMDb account. + /// + public let account: any AccountService + + /// + /// TMDb authentication. + /// + public let authentication: any AuthenticationService + + /// + /// TMDb certifications. + /// + public let certifications: any CertificationService + + /// + /// TMDb companies. + /// + public let companies: any CompanyService + + /// + /// TMDb configuration. + /// + public let configurations: any ConfigurationService + + /// + /// TMDb discover. + /// + public let discover: any DiscoverService + + /// + /// TMDb genres. + /// + public let genres: any GenreService + + /// + /// TMDb movies. + /// + public let movies: any MovieService + + /// + /// TMDb people. + /// + public let people: any PersonService + + /// + /// TMDb search. + /// + public let search: any SearchService + + /// + /// TMDb trending. + /// + public let trending: any TrendingService + + /// + /// TMDb TV episodes. + /// + public let tvEpisodes: any TVEpisodeService + + /// + /// TMDb TV seasons. + /// + public let tvSeasons: any TVSeasonService + + /// + /// TMDb TV series. + /// + public let tvSeries: any TVSeriesService + + /// + /// TMDb watch providers. + /// + public let watchProviders: any WatchProviderService + + /// + /// Creates a TMDb client using `URLSession` as the `HTTPClient`. + /// + /// - Parameters: + /// - apiKey: The TMDb API key to use. + /// + public convenience init(apiKey: String) { + self.init( + apiKey: apiKey, + httpClient: TMDbFactory.defaultHTTPClientAdapter() + ) + } + + /// + /// Creates a TMDb client. + /// + /// - Parameters: + /// - apiKey: The TMDb API key to use. + /// - httpClient: A custom HTTP client adapter for making HTTP requests. + /// + public convenience init(apiKey: String, httpClient: some HTTPClient) { + let apiClient = TMDbFactory.apiClient(apiKey: apiKey, httpClient: httpClient) + let authAPIClient = TMDbFactory.authAPIClient(apiKey: apiKey, httpClient: httpClient) + let authenticateURLBuilder = TMDbFactory.authenticateURLBuilder() + + self.init( + accountService: TMDbAccountService(apiClient: apiClient), + authenticationService: TMDbAuthenticationService( + apiClient: authAPIClient, + authenticateURLBuilder: authenticateURLBuilder + ), + certificationService: TMDbCertificationService(apiClient: apiClient), + companyService: TMDbCompanyService(apiClient: apiClient), + configurationService: TMDbConfigurationService(apiClient: apiClient), + discoverService: TMDbDiscoverService(apiClient: apiClient), + genreService: TMDbGenreService(apiClient: apiClient), + movieService: TMDbMovieService(apiClient: apiClient), + personService: TMDbPersonService(apiClient: apiClient), + searchService: TMDbSearchService(apiClient: apiClient), + trendingService: TMDbTrendingService(apiClient: apiClient), + tvEpisodeService: TMDbTVEpisodeService(apiClient: apiClient), + tvSeaonService: TMDbTVSeasonService(apiClient: apiClient), + tvSeriesService: TMDbTVSeriesService(apiClient: apiClient), + watchProviderService: TMDbWatchProviderService(apiClient: apiClient) + ) + } + + init( + accountService: some AccountService, + authenticationService: some AuthenticationService, + certificationService: some CertificationService, + companyService: some CompanyService, + configurationService: some ConfigurationService, + discoverService: some DiscoverService, + genreService: some GenreService, + movieService: some MovieService, + personService: some PersonService, + searchService: some SearchService, + trendingService: some TrendingService, + tvEpisodeService: some TVEpisodeService, + tvSeaonService: some TVSeasonService, + tvSeriesService: some TVSeriesService, + watchProviderService: some WatchProviderService + ) { + self.account = accountService + self.authentication = authenticationService + self.certifications = certificationService + self.companies = companyService + self.configurations = configurationService + self.discover = discoverService + self.genres = genreService + self.movies = movieService + self.people = personService + self.search = searchService + self.trending = trendingService + self.tvEpisodes = tvEpisodeService + self.tvSeasons = tvSeaonService + self.tvSeries = tvSeriesService + self.watchProviders = watchProviderService + } + +} diff --git a/Sources/TMDb/TMDb.docc/Extensions/AccountService.md b/Sources/TMDb/TMDb.docc/Extensions/AccountService.md index 43f2a890..3d85ca49 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/AccountService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/AccountService.md @@ -1,11 +1,7 @@ -# ``TMDb/AccountService`` +# ``AccountService`` ## Topics -### Creating an Account Service - -- ``init(configuration:)`` - ### Account Details - ``details(session:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md b/Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md index 8b6a4492..8cc8019f 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/AuthenticationService.md @@ -1,11 +1,7 @@ -# ``TMDb/AuthenticationService`` +# ``AuthenticationService`` ## Topics -### Creating an Authentication Service - -- ``init(configuration:)`` - ### Creating Sessions - ``guestSession()`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/CertificationService.md b/Sources/TMDb/TMDb.docc/Extensions/CertificationService.md index f2918625..1f01aec7 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/CertificationService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/CertificationService.md @@ -1,11 +1,7 @@ -# ``TMDb/CertificationService`` +# ``CertificationService`` ## Topics -### Creating a Certification Service - -- ``init(configuration:)`` - ### Certifications - ``movieCertifications()`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/CompanyService.md b/Sources/TMDb/TMDb.docc/Extensions/CompanyService.md index 0ee16e18..080ddbea 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/CompanyService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/CompanyService.md @@ -1,11 +1,7 @@ -# ``TMDb/CompanyService`` +# ``CompanyService`` ## Topics -### Creating a Company Service - -- ``init(configuration:)`` - ### Company Details - ``details(forCompany:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/ConfigurationService.md b/Sources/TMDb/TMDb.docc/Extensions/ConfigurationService.md index 5d89ce37..9db1c8f2 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/ConfigurationService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/ConfigurationService.md @@ -1,11 +1,7 @@ -# ``TMDb/ConfigurationService`` +# ``ConfigurationService`` ## Topics -### Creating a Configuration Service - -- ``init(configuration:)`` - ### API Configuration - ``apiConfiguration()`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/DiscoverService.md b/Sources/TMDb/TMDb.docc/Extensions/DiscoverService.md index bf7fe620..bade5d22 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/DiscoverService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/DiscoverService.md @@ -1,11 +1,7 @@ -# ``TMDb/DiscoverService`` +# ``DiscoverService`` ## Topics -### Creating a Discover Service - -- ``init(configuration:)`` - ### Discover Movies - ``movies(filter:sortedBy:page:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/GenreService.md b/Sources/TMDb/TMDb.docc/Extensions/GenreService.md index 23a6f917..3aa55b1a 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/GenreService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/GenreService.md @@ -1,11 +1,7 @@ -# ``TMDb/GenreService`` +# ``GenreService`` ## Topics -### Creating a Genre Service - -- ``init(configuration:)`` - ### Movie Genres - ``movieGenres(language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/MovieService.md b/Sources/TMDb/TMDb.docc/Extensions/MovieService.md index ecea1095..4d096b98 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/MovieService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/MovieService.md @@ -1,11 +1,7 @@ -# ``TMDb/MovieService`` +# ``MovieService`` ## Topics -### Creating a Movie Service - -- ``init(configuration:)`` - ### Details - ``details(forMovie:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/PersonService.md b/Sources/TMDb/TMDb.docc/Extensions/PersonService.md index be237a58..a978ee14 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/PersonService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/PersonService.md @@ -1,11 +1,7 @@ -# ``TMDb/PersonService`` +# ``PersonService`` ## Topics -### Creating a Person Service - -- ``init(configuration:)`` - ### Details - ``details(forPerson:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/SearchService.md b/Sources/TMDb/TMDb.docc/Extensions/SearchService.md index 8261a4ec..6ab5bd77 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/SearchService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/SearchService.md @@ -1,11 +1,7 @@ -# ``TMDb/SearchService`` +# ``SearchService`` ## Topics -### Creating a Search Service - -- ``init(configuration:)`` - ### Media - ``searchAll(query:filter:page:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/TMDbClient.md b/Sources/TMDb/TMDb.docc/Extensions/TMDbClient.md new file mode 100644 index 00000000..8ba1c6d2 --- /dev/null +++ b/Sources/TMDb/TMDb.docc/Extensions/TMDbClient.md @@ -0,0 +1,26 @@ +# ``TMDbClient`` + +## Topics + +### Creating a TMDBClient + +- ``init(apiKey:)`` +- ``init(apiKey:httpClient:)`` + +### TMDb Areas + +- ``movies`` +- ``tvSeries`` +- ``tvSeasons`` +- ``tvEpisodes`` +- ``people`` +- ``discover`` +- ``trending`` +- ``search`` +- ``certifications`` +- ``companies`` +- ``genres`` +- ``watchProviders`` +- ``configurations`` +- ``account`` +- ``authentication`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/TVEpisodeService.md b/Sources/TMDb/TMDb.docc/Extensions/TVEpisodeService.md index 14bed387..558a7e18 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/TVEpisodeService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/TVEpisodeService.md @@ -1,11 +1,7 @@ -# ``TMDb/TVEpisodeService`` +# ``TVEpisodeService`` ## Topics -### Creating a TV Episode Service - -- ``init(configuration:)`` - ### Details - ``details(forEpisode:inSeason:inTVSeries:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/TVSeasonService.md b/Sources/TMDb/TMDb.docc/Extensions/TVSeasonService.md index 700fc26c..b91e4025 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/TVSeasonService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/TVSeasonService.md @@ -1,11 +1,7 @@ -# ``TMDb/TVSeasonService`` +# ``TVSeasonService`` ## Topics -### Creating a TV Season Service - -- ``init(configuration:)`` - ### Details - ``details(forSeason:inTVSeries:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/TVSeriesService.md b/Sources/TMDb/TMDb.docc/Extensions/TVSeriesService.md index 426c196b..f647bf00 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/TVSeriesService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/TVSeriesService.md @@ -1,11 +1,7 @@ -# ``TMDb/TVSeriesService`` +# ``TVSeriesService`` ## Topics -### Creating a TV Series Service - -- ``init(configuration:)`` - ### Details - ``details(forTVSeries:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/TrendingService.md b/Sources/TMDb/TMDb.docc/Extensions/TrendingService.md index 756f082e..97a2e2af 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/TrendingService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/TrendingService.md @@ -1,11 +1,7 @@ -# ``TMDb/TrendingService`` +# ``TrendingService`` ## Topics -### Creating a Trending Service - -- ``init(configuration:)`` - ### Movies - ``movies(inTimeWindow:page:language:)`` diff --git a/Sources/TMDb/TMDb.docc/Extensions/WatchProviderService.md b/Sources/TMDb/TMDb.docc/Extensions/WatchProviderService.md index f2984774..a93bec52 100644 --- a/Sources/TMDb/TMDb.docc/Extensions/WatchProviderService.md +++ b/Sources/TMDb/TMDb.docc/Extensions/WatchProviderService.md @@ -1,11 +1,7 @@ -# ``TMDb/WatchProviderService`` +# ``WatchProviderService`` ## Topics -### Creating a Watch Provider Service - -- ``init(configuration:)`` - ### Providers - ``movieWatchProviders(filter:language:)`` diff --git a/Sources/TMDb/TMDb.docc/GettingStarted/ConfiguringTMDb.md b/Sources/TMDb/TMDb.docc/GettingStarted/ConfiguringTMDb.md deleted file mode 100644 index fc674b76..00000000 --- a/Sources/TMDb/TMDb.docc/GettingStarted/ConfiguringTMDb.md +++ /dev/null @@ -1,46 +0,0 @@ -# Configuring TMDb - -Instructions on how to configure TMDb before using services. - -## Overview - -Before creating any of the TMDb services a configuration needs to be created. - -## Creating the Configuration object - -To create a configuration object you first need a TMDb API Key. See . - -### Configuration with URLSession - -To use `URLSession` to perform network tasks, create a configuration object. - -```swift -let tmdbConfiguration = TMDbConfiguration(apiKey: "") -``` - -### Configuration with custom HTTP Client - -TMDb can be configured with your own adapter to perform network tasks. It allows -you to use an HTTP client library such as -[``AsyncHTTPClient``](https://github.com/swift-server/async-http-client). - -The adapter should conform to ``HTTPClient``. - -```swift -class MyHTTPClient: HTTPClient { - func perform(request: HTTPRequest) async throws -> HTTPResponse { - // Implement performing a network request. - } -} -``` - -Then create a TMDb configuration using the adapter. - -```swift -let customHTTPClient = MyHTTPClient() - -let tmdbConfiguration = TMDbConfiguration( - apiKey: "", - httpClient: customHTTPClient -) -``` diff --git a/Sources/TMDb/TMDb.docc/GettingStarted/CreatingTMDbClient.md b/Sources/TMDb/TMDb.docc/GettingStarted/CreatingTMDbClient.md new file mode 100644 index 00000000..417f252c --- /dev/null +++ b/Sources/TMDb/TMDb.docc/GettingStarted/CreatingTMDbClient.md @@ -0,0 +1,62 @@ +# Creating a TMDbClient + +Instructions on how to setup TMDbClient. + +## Overview + +Create a ``TMDbClient`` to request data from TMDb. + +## Creating a TMDb API Key + +To create a configuration object you first need a TMDb API Key. See . + +## Creating TMDbClient + +### Configuration with URLSession + +To use `URLSession` to perform network tasks, create the ``TMDbClient`` + +```swift +let tmdbClient = TMDbClient(apiKey: "") +``` + +### Configuration with custom HTTP Client + +TMDb can be configured with your own adapter to perform network tasks. It allows +you to use an HTTP client library such as +[``AsyncHTTPClient``](https://github.com/swift-server/async-http-client). + +The adapter should conform to ``HTTPClient``. + +```swift +class MyHTTPClient: HTTPClient { + func perform(request: HTTPRequest) async throws -> HTTPResponse { + // Implement performing a network request. + } +} +``` + +Then create `TMDbClient` using the adapter. + +```swift +let customHTTPClient = MyHTTPClient() + +let tmdbClient = TMDbClient( + apiKey: "", + httpClient: customHTTPClient +) +``` + +## Using TMDbClient + +Once created, your instance of ``TMDbClient`` can be used to interact with the +TMDb API. + +e.g. To discover movies and fetch details on a specific movie + +```swift +let tmdbClient = TMDbClient(apiKey: "") + +let moviesToDiscover = try await tmdbClient.discover.movies().results +let fightClub = try await tmdbClient.movies.details(forMovie: 550) +``` diff --git a/Sources/TMDb/TMDb.docc/TMDb.md b/Sources/TMDb/TMDb.docc/TMDb.md index e90b3745..50ce3bd0 100644 --- a/Sources/TMDb/TMDb.docc/TMDb.md +++ b/Sources/TMDb/TMDb.docc/TMDb.md @@ -43,10 +43,9 @@ Watch providers provided by [JustWatch](https://www.justwatch.com). ### Getting Started +- - -- -- ``TMDbConfiguration`` -- ``HTTPClient`` +- ``TMDbClient`` ### API Configuration @@ -204,3 +203,9 @@ Watch providers provided by [JustWatch](https://www.justwatch.com). - ``Credential`` - ``Token`` - ``Session`` + +### Custom HTTP Client + +- ``HTTPClient`` +- ``HTTPRequest`` +- ``HTTPResponse`` diff --git a/Sources/TMDb/TMDbConfiguration.swift b/Sources/TMDb/TMDbConfiguration.swift deleted file mode 100644 index fe079b4d..00000000 --- a/Sources/TMDb/TMDbConfiguration.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// TMDbConfiguration.swift -// TMDb -// -// Copyright © 2024 Adam Young. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an AS IS BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -/// -/// A model representing configuration settings for TMDb. -/// -/// Create an API key at [https://www.themoviedb.org/documentation/api](https://www.themoviedb.org/documentation/api). -/// -public final class TMDbConfiguration: ConfigurationProviding, Sendable { - - /// - /// TMDb API key. - /// - public let apiKey: String - - /// - /// The HTTP client adapter for making HTTP requests. - /// - public let httpClient: any HTTPClient - - /// - /// Creates a TMDb configuration object using URLSession as the HTTP client. - /// - /// - Parameters: - /// - apiKey: The TMDb API key to use. - /// - public convenience init(apiKey: String) { - self.init( - apiKey: apiKey, - httpClient: TMDbFactory.defaultHTTPClientAdapter() - ) - } - - /// - /// Creates a TMDb configuration object. - /// - /// - Parameters: - /// - apiKey: The TMDb API key to use. - /// - httpClient: A custom HTTP client adapter for making HTTP requests. - /// - public init( - apiKey: String, - httpClient: some HTTPClient - ) { - self.apiKey = apiKey - self.httpClient = httpClient - } - -} diff --git a/Sources/TMDb/TMDbFactory.swift b/Sources/TMDb/TMDbFactory.swift index 495a3d3e..390a55c3 100644 --- a/Sources/TMDb/TMDbFactory.swift +++ b/Sources/TMDb/TMDbFactory.swift @@ -35,21 +35,21 @@ final class TMDbFactory { extension TMDbFactory { - static func apiClient(configuration: some ConfigurationProviding) -> some APIClient { + static func apiClient(apiKey: String, httpClient: some HTTPClient) -> some APIClient { TMDbAPIClient( - apiKey: configuration.apiKey, + apiKey: apiKey, baseURL: tmdbAPIBaseURL, serialiser: serialiser(), - httpClient: configuration.httpClient + httpClient: httpClient ) } - static func authAPIClient(configuration: some ConfigurationProviding) -> some APIClient { + static func authAPIClient(apiKey: String, httpClient: some HTTPClient) -> some APIClient { TMDbAPIClient( - apiKey: configuration.apiKey, + apiKey: apiKey, baseURL: .tmdbAPIBaseURL, serialiser: authSerialiser(), - httpClient: configuration.httpClient + httpClient: httpClient ) } diff --git a/Tests/TMDbIntegrationTests/AccountIntegrationTests.swift b/Tests/TMDbIntegrationTests/AccountIntegrationTests.swift index 16976cfa..3d0533e4 100644 --- a/Tests/TMDbIntegrationTests/AccountIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/AccountIntegrationTests.swift @@ -22,17 +22,20 @@ import XCTest final class AccountIntegrationTests: XCTestCase { - var accountService: AccountService! - var authenticationService: AuthenticationService! + var accountService: (any AccountService)! + var authenticationService: (any AuthenticationService)! var session: Session! override func setUp() async throws { try await super.setUp() - let configuration = try tmdbConfiguration() - authenticationService = AuthenticationService(configuration: configuration) + let apiKey = try tmdbAPIKey() + let tmdbClient = TMDbClient(apiKey: apiKey) let credential = try tmdbCredential() + + authenticationService = tmdbClient.authentication + accountService = tmdbClient.account + session = try await authenticationService.createSession(withCredential: credential) - accountService = AccountService(configuration: configuration) } override func tearDown() async throws { diff --git a/Tests/TMDbIntegrationTests/AuthenticationIntegrationTests.swift b/Tests/TMDbIntegrationTests/AuthenticationIntegrationTests.swift index bc58ae38..1107c115 100644 --- a/Tests/TMDbIntegrationTests/AuthenticationIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/AuthenticationIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class AuthenticationIntegrationTests: XCTestCase { - var authenticationService: AuthenticationService! + var authenticationService: (any AuthenticationService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - authenticationService = AuthenticationService(configuration: configuration) + let apiKey = try tmdbAPIKey() + authenticationService = TMDbClient(apiKey: apiKey).authentication } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/CertificationIntegrationTests.swift b/Tests/TMDbIntegrationTests/CertificationIntegrationTests.swift index d16413cc..e6408d21 100644 --- a/Tests/TMDbIntegrationTests/CertificationIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/CertificationIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class CertificationIntegrationTests: XCTestCase { - var certificationService: CertificationService! + var certificationService: (any CertificationService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - certificationService = CertificationService(configuration: configuration) + let apiKey = try tmdbAPIKey() + certificationService = TMDbClient(apiKey: apiKey).certifications } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/CompanyIntegrationTests.swift b/Tests/TMDbIntegrationTests/CompanyIntegrationTests.swift index ed9254ec..263d3153 100644 --- a/Tests/TMDbIntegrationTests/CompanyIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/CompanyIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class CompanyIntegrationTests: XCTestCase { - var companyService: CompanyService! + var companyService: (any CompanyService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - companyService = CompanyService(configuration: configuration) + let apiKey = try tmdbAPIKey() + companyService = TMDbClient(apiKey: apiKey).companies } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/ConfigurationIntegrationTests.swift b/Tests/TMDbIntegrationTests/ConfigurationIntegrationTests.swift index b0c63760..9eb2fefd 100644 --- a/Tests/TMDbIntegrationTests/ConfigurationIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/ConfigurationIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class ConfigurationIntegrationTests: XCTestCase { - var configurationService: ConfigurationService! + var configurationService: (any ConfigurationService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - configurationService = ConfigurationService(configuration: configuration) + let apiKey = try tmdbAPIKey() + configurationService = TMDbClient(apiKey: apiKey).configurations } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/DiscoverIntegrationTests.swift b/Tests/TMDbIntegrationTests/DiscoverIntegrationTests.swift index fd5763d3..1a8c046b 100644 --- a/Tests/TMDbIntegrationTests/DiscoverIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/DiscoverIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class DiscoverIntegrationTests: XCTestCase { - var discoverService: DiscoverService! + var discoverService: (any DiscoverService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - discoverService = DiscoverService(configuration: configuration) + let apiKey = try tmdbAPIKey() + discoverService = TMDbClient(apiKey: apiKey).discover } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/GenreIntegrationTests.swift b/Tests/TMDbIntegrationTests/GenreIntegrationTests.swift index 9c195261..2d127d27 100644 --- a/Tests/TMDbIntegrationTests/GenreIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/GenreIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class GenreIntegrationTests: XCTestCase { - var genreService: GenreService! + var genreService: (any GenreService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - genreService = GenreService(configuration: configuration) + let apiKey = try tmdbAPIKey() + genreService = TMDbClient(apiKey: apiKey).genres } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/MovieIntegrationTests.swift b/Tests/TMDbIntegrationTests/MovieIntegrationTests.swift index 0644057b..55f26754 100644 --- a/Tests/TMDbIntegrationTests/MovieIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/MovieIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class MovieIntegrationTests: XCTestCase { - var movieService: MovieService! + var movieService: (any MovieService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - movieService = MovieService(configuration: configuration) + let apiKey = try tmdbAPIKey() + movieService = TMDbClient(apiKey: apiKey).movies } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/PersonIntegrationTests.swift b/Tests/TMDbIntegrationTests/PersonIntegrationTests.swift index 5b3bfaa9..4e5078bd 100644 --- a/Tests/TMDbIntegrationTests/PersonIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/PersonIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class PersonIntegrationTests: XCTestCase { - var personService: PersonService! + var personService: (any PersonService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - personService = PersonService(configuration: configuration) + let apiKey = try tmdbAPIKey() + personService = TMDbClient(apiKey: apiKey).people } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/SearchIntegrationTests.swift b/Tests/TMDbIntegrationTests/SearchIntegrationTests.swift index 7dda1ab3..cc55d92a 100644 --- a/Tests/TMDbIntegrationTests/SearchIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/SearchIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class SearchIntegrationTests: XCTestCase { - var searchService: SearchService! + var searchService: (any SearchService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - searchService = SearchService(configuration: configuration) + let apiKey = try tmdbAPIKey() + searchService = TMDbClient(apiKey: apiKey).search } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/TVEpisodeServiceTests.swift b/Tests/TMDbIntegrationTests/TVEpisodeServiceTests.swift index 1077352b..b3291939 100644 --- a/Tests/TMDbIntegrationTests/TVEpisodeServiceTests.swift +++ b/Tests/TMDbIntegrationTests/TVEpisodeServiceTests.swift @@ -22,12 +22,12 @@ import XCTest final class TVEpisodeServiceTests: XCTestCase { - var tvEpisodeService: TVEpisodeService! + var tvEpisodeService: (any TVEpisodeService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - tvEpisodeService = TVEpisodeService(configuration: configuration) + let apiKey = try tmdbAPIKey() + tvEpisodeService = TMDbClient(apiKey: apiKey).tvEpisodes } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/TVSeasonService.swift b/Tests/TMDbIntegrationTests/TVSeasonIntegrationTests.swift similarity index 89% rename from Tests/TMDbIntegrationTests/TVSeasonService.swift rename to Tests/TMDbIntegrationTests/TVSeasonIntegrationTests.swift index 8262ef57..17a0deb8 100644 --- a/Tests/TMDbIntegrationTests/TVSeasonService.swift +++ b/Tests/TMDbIntegrationTests/TVSeasonIntegrationTests.swift @@ -1,5 +1,5 @@ // -// TVSeasonService.swift +// TVSeasonIntegrationTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,14 +20,14 @@ import TMDb import XCTest -final class TVSeasonServiceTests: XCTestCase { +final class TVSeasonIntegrationTests: XCTestCase { - var tvSeasonService: TVSeasonService! + var tvSeasonService: (any TVSeasonService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - tvSeasonService = TVSeasonService(configuration: configuration) + let apiKey = try tmdbAPIKey() + tvSeasonService = TMDbClient(apiKey: apiKey).tvSeasons } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift b/Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift index e2864b38..9c37897b 100644 --- a/Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift +++ b/Tests/TMDbIntegrationTests/TVSeriesServiceTests.swift @@ -22,12 +22,12 @@ import XCTest final class TVSeriesServiceTests: XCTestCase { - var tvSeriesService: TVSeriesService! + var tvSeriesService: (any TVSeriesService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - tvSeriesService = TVSeriesService(configuration: configuration) + let apiKey = try tmdbAPIKey() + tvSeriesService = TMDbClient(apiKey: apiKey).tvSeries } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/TrendingIntegrationTests.swift b/Tests/TMDbIntegrationTests/TrendingIntegrationTests.swift index fce7abff..f15f3555 100644 --- a/Tests/TMDbIntegrationTests/TrendingIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/TrendingIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class TrendingIntegrationTests: XCTestCase { - var trendingService: TrendingService! + var trendingService: (any TrendingService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - trendingService = TrendingService(configuration: configuration) + let apiKey = try tmdbAPIKey() + trendingService = TMDbClient(apiKey: apiKey).trending } override func tearDown() { diff --git a/Tests/TMDbIntegrationTests/Utility/XCTestCase+ConfigureTMDb.swift b/Tests/TMDbIntegrationTests/Utility/XCTestCase+ConfigureTMDb.swift index 946a3b5f..cb2fc624 100644 --- a/Tests/TMDbIntegrationTests/Utility/XCTestCase+ConfigureTMDb.swift +++ b/Tests/TMDbIntegrationTests/Utility/XCTestCase+ConfigureTMDb.swift @@ -22,10 +22,6 @@ import XCTest extension XCTestCase { - func tmdbConfiguration() throws -> TMDbConfiguration { - try TMDbConfiguration(apiKey: tmdbAPIKey()) - } - func tmdbCredential() throws -> Credential { try Self.tmdbCredential() } @@ -34,11 +30,11 @@ extension XCTestCase { extension XCTestCase { - private func tmdbAPIKey() throws -> String { + func tmdbAPIKey() throws -> String { try Self.tmdbAPIKey() } - private static func tmdbAPIKey() throws -> String { + static func tmdbAPIKey() throws -> String { guard let apiKey = ProcessInfo.processInfo.environment["TMDB_API_KEY"], !apiKey.isEmpty diff --git a/Tests/TMDbIntegrationTests/WatchProviderIntegrationTests.swift b/Tests/TMDbIntegrationTests/WatchProviderIntegrationTests.swift index 043db7f2..9bd80b55 100644 --- a/Tests/TMDbIntegrationTests/WatchProviderIntegrationTests.swift +++ b/Tests/TMDbIntegrationTests/WatchProviderIntegrationTests.swift @@ -22,12 +22,12 @@ import XCTest final class WatchProviderIntegrationTests: XCTestCase { - var watchProviderService: WatchProviderService! + var watchProviderService: (any WatchProviderService)! override func setUpWithError() throws { try super.setUpWithError() - let configuration = try tmdbConfiguration() - watchProviderService = WatchProviderService(configuration: configuration) + let apiKey = try tmdbAPIKey() + watchProviderService = TMDbClient(apiKey: apiKey).watchProviders } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/APIClient/MockAPIClient.swift b/Tests/TMDbTests/Domain/APIClient/MockAPIClient.swift index db85da09..64492cbd 100644 --- a/Tests/TMDbTests/Domain/APIClient/MockAPIClient.swift +++ b/Tests/TMDbTests/Domain/APIClient/MockAPIClient.swift @@ -20,7 +20,7 @@ @testable import TMDb import XCTest -final class MockAPIClient: APIClient { +final class MockAPIClient: APIClient, @unchecked Sendable { private(set) var requests: [any APIRequest] = [] diff --git a/Tests/TMDbTests/Domain/Services/Account/FavouriteSortTests.swift b/Tests/TMDbTests/Domain/Services/Account/Sorts/FavouriteSortTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Account/FavouriteSortTests.swift rename to Tests/TMDbTests/Domain/Services/Account/Sorts/FavouriteSortTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Account/WatchlistSortTests.swift b/Tests/TMDbTests/Domain/Services/Account/Sorts/WatchlistSortTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Account/WatchlistSortTests.swift rename to Tests/TMDbTests/Domain/Services/Account/Sorts/WatchlistSortTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Account/AccountServiceDetailsTests.swift b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceDetailsTests.swift similarity index 90% rename from Tests/TMDbTests/Domain/Services/Account/AccountServiceDetailsTests.swift rename to Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceDetailsTests.swift index e05515c6..9a60d125 100644 --- a/Tests/TMDbTests/Domain/Services/Account/AccountServiceDetailsTests.swift +++ b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceDetailsTests.swift @@ -1,5 +1,5 @@ // -// AccountServiceDetailsTests.swift +// TMDbAccountServiceDetailsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,9 +20,9 @@ @testable import TMDb import XCTest -final class AccountServiceDetails: XCTestCase { +final class TMDbAccountServiceDetails: XCTestCase { - var service: AccountService! + var service: TMDbAccountService! var apiClient: MockAPIClient! var session: Session! @@ -30,7 +30,7 @@ final class AccountServiceDetails: XCTestCase { super.setUp() session = Session(success: true, sessionID: "abc123") apiClient = MockAPIClient() - service = AccountService(apiClient: apiClient) + service = TMDbAccountService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Account/AccountServiceFavouriteTests.swift b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceFavouriteTests.swift similarity index 97% rename from Tests/TMDbTests/Domain/Services/Account/AccountServiceFavouriteTests.swift rename to Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceFavouriteTests.swift index a86061ce..09b3cf37 100644 --- a/Tests/TMDbTests/Domain/Services/Account/AccountServiceFavouriteTests.swift +++ b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceFavouriteTests.swift @@ -1,5 +1,5 @@ // -// AccountServiceFavouriteTests.swift +// TMDbAccountServiceFavouriteTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,9 +20,9 @@ @testable import TMDb import XCTest -final class AccountFavouriteServiceTests: XCTestCase { +final class TMDbAccountFavouriteServiceTests: XCTestCase { - var service: AccountService! + var service: TMDbAccountService! var apiClient: MockAPIClient! var session: Session! @@ -30,7 +30,7 @@ final class AccountFavouriteServiceTests: XCTestCase { super.setUp() session = Session(success: true, sessionID: "abc123") apiClient = MockAPIClient() - service = AccountService(apiClient: apiClient) + service = TMDbAccountService(apiClient: apiClient) } override func tearDown() { @@ -42,7 +42,7 @@ final class AccountFavouriteServiceTests: XCTestCase { } -extension AccountFavouriteServiceTests { +extension TMDbAccountFavouriteServiceTests { func testFavouriteMoviesReturnsMoviesList() async throws { let accountID = 123 @@ -218,7 +218,7 @@ extension AccountFavouriteServiceTests { } -extension AccountFavouriteServiceTests { +extension TMDbAccountFavouriteServiceTests { func testFavouriteTVSeriesReturnsTVSeriesList() async throws { let accountID = 123 diff --git a/Tests/TMDbTests/Domain/Services/Account/AccountServiceWatchlistTests.swift b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceWatchlistTests.swift similarity index 97% rename from Tests/TMDbTests/Domain/Services/Account/AccountServiceWatchlistTests.swift rename to Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceWatchlistTests.swift index 177beaf7..36520c17 100644 --- a/Tests/TMDbTests/Domain/Services/Account/AccountServiceWatchlistTests.swift +++ b/Tests/TMDbTests/Domain/Services/Account/TMDbAccountServiceWatchlistTests.swift @@ -1,5 +1,5 @@ // -// AccountServiceWatchlistTests.swift +// TMDbAccountServiceWatchlistTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,9 +20,9 @@ @testable import TMDb import XCTest -final class AccountServiceWatchlistTests: XCTestCase { +final class TMDbAccountServiceWatchlistTests: XCTestCase { - var service: AccountService! + var service: TMDbAccountService! var apiClient: MockAPIClient! var session: Session! @@ -30,7 +30,7 @@ final class AccountServiceWatchlistTests: XCTestCase { super.setUp() session = Session(success: true, sessionID: "abc123") apiClient = MockAPIClient() - service = AccountService(apiClient: apiClient) + service = TMDbAccountService(apiClient: apiClient) } override func tearDown() { @@ -42,7 +42,7 @@ final class AccountServiceWatchlistTests: XCTestCase { } -extension AccountServiceWatchlistTests { +extension TMDbAccountServiceWatchlistTests { func testMovieWatchlistReturnsMoviesList() async throws { let accountID = 123 @@ -218,7 +218,7 @@ extension AccountServiceWatchlistTests { } -extension AccountServiceWatchlistTests { +extension TMDbAccountServiceWatchlistTests { func testTVSeriesWatchlistReturnsTVSeriesList() async throws { let accountID = 123 diff --git a/Tests/TMDbTests/Domain/Services/Authentication/AuthenticateURLMockBuilder.swift b/Tests/TMDbTests/Domain/Services/Authentication/AuthenticateURLMockBuilder.swift index de8be9ff..753e8414 100644 --- a/Tests/TMDbTests/Domain/Services/Authentication/AuthenticateURLMockBuilder.swift +++ b/Tests/TMDbTests/Domain/Services/Authentication/AuthenticateURLMockBuilder.swift @@ -20,12 +20,14 @@ import Foundation @testable import TMDb -final class AuthenticateURLMockBuilder: AuthenticateURLBuilding { +final class AuthenticateURLMockBuilder: AuthenticateURLBuilding, @unchecked Sendable { var authenticateURLResult: URL = .init(string: "https://some.domain.com/authenticate")! private(set) var lastRequestToken: String? private(set) var lastRedirectURL: URL? + init() {} + func authenticateURL(with requestToken: String) -> URL { authenticateURL(with: requestToken, redirectURL: nil) } diff --git a/Tests/TMDbTests/Domain/Services/Authentication/AuthenticationServiceTests.swift b/Tests/TMDbTests/Domain/Services/Authentication/TMDbAuthenticationServiceTests.swift similarity index 97% rename from Tests/TMDbTests/Domain/Services/Authentication/AuthenticationServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Authentication/TMDbAuthenticationServiceTests.swift index 93e115f2..639859be 100644 --- a/Tests/TMDbTests/Domain/Services/Authentication/AuthenticationServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Authentication/TMDbAuthenticationServiceTests.swift @@ -1,5 +1,5 @@ // -// AuthenticationServiceTests.swift +// TMDbAuthenticationServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,9 +20,9 @@ @testable import TMDb import XCTest -final class AuthenticationServiceTests: XCTestCase { +final class TMDbAuthenticationServiceTests: XCTestCase { - var service: AuthenticationService! + var service: TMDbAuthenticationService! var apiClient: MockAPIClient! var authenticateURLBuilder: AuthenticateURLMockBuilder! @@ -30,7 +30,7 @@ final class AuthenticationServiceTests: XCTestCase { super.setUp() apiClient = MockAPIClient() authenticateURLBuilder = AuthenticateURLMockBuilder() - service = AuthenticationService(apiClient: apiClient, authenticateURLBuilder: authenticateURLBuilder) + service = TMDbAuthenticationService(apiClient: apiClient, authenticateURLBuilder: authenticateURLBuilder) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Certifications/CertificationServiceTests.swift b/Tests/TMDbTests/Domain/Services/Certifications/TMDbCertificationServiceTests.swift similarity index 93% rename from Tests/TMDbTests/Domain/Services/Certifications/CertificationServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Certifications/TMDbCertificationServiceTests.swift index 4246d256..649deabf 100644 --- a/Tests/TMDbTests/Domain/Services/Certifications/CertificationServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Certifications/TMDbCertificationServiceTests.swift @@ -1,5 +1,5 @@ // -// CertificationServiceTests.swift +// TMDbCertificationServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class CertificationServiceTests: XCTestCase { +final class TMDbCertificationServiceTests: XCTestCase { - var service: CertificationService! + var service: TMDbCertificationService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = CertificationService(apiClient: apiClient) + service = TMDbCertificationService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Company/Requests/CompanyDetailsRequestTests.swift b/Tests/TMDbTests/Domain/Services/Companies/Requests/CompanyDetailsRequestTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Company/Requests/CompanyDetailsRequestTests.swift rename to Tests/TMDbTests/Domain/Services/Companies/Requests/CompanyDetailsRequestTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Company/CompanyServiceTests.swift b/Tests/TMDbTests/Domain/Services/Companies/TMDbCompanyServiceTests.swift similarity index 90% rename from Tests/TMDbTests/Domain/Services/Company/CompanyServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Companies/TMDbCompanyServiceTests.swift index e9fb3dd2..93b78527 100644 --- a/Tests/TMDbTests/Domain/Services/Company/CompanyServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Companies/TMDbCompanyServiceTests.swift @@ -1,5 +1,5 @@ // -// CompanyServiceTests.swift +// TMDbCompanyServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class CompanyServiceTests: XCTestCase { +final class TMDbCompanyServiceTests: XCTestCase { - var service: CompanyService! + var service: TMDbCompanyService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = CompanyService(apiClient: apiClient) + service = TMDbCompanyService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Configuration/ConfigurationServiceTests.swift b/Tests/TMDbTests/Domain/Services/Configuration/TMDbConfigurationServiceTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/Configuration/ConfigurationServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Configuration/TMDbConfigurationServiceTests.swift index e3420a3a..fcf911bc 100644 --- a/Tests/TMDbTests/Domain/Services/Configuration/ConfigurationServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Configuration/TMDbConfigurationServiceTests.swift @@ -1,5 +1,5 @@ // -// ConfigurationServiceTests.swift +// TMDbConfigurationServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class ConfigurationServiceTests: XCTestCase { +final class TMDbConfigurationServiceTests: XCTestCase { - var service: ConfigurationService! + var service: TMDbConfigurationService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = ConfigurationService(apiClient: apiClient) + service = TMDbConfigurationService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Discover/MovieSortTests.swift b/Tests/TMDbTests/Domain/Services/Discover/Sorts/MovieSortTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Discover/MovieSortTests.swift rename to Tests/TMDbTests/Domain/Services/Discover/Sorts/MovieSortTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Discover/TVSeriesSortTests.swift b/Tests/TMDbTests/Domain/Services/Discover/Sorts/TVSeriesSortTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Discover/TVSeriesSortTests.swift rename to Tests/TMDbTests/Domain/Services/Discover/Sorts/TVSeriesSortTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Discover/DiscoverServiceTests.swift b/Tests/TMDbTests/Domain/Services/Discover/TMDbDiscoverServiceTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/Discover/DiscoverServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Discover/TMDbDiscoverServiceTests.swift index 59ebbc9f..52af3bb3 100644 --- a/Tests/TMDbTests/Domain/Services/Discover/DiscoverServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Discover/TMDbDiscoverServiceTests.swift @@ -1,5 +1,5 @@ // -// DiscoverServiceTests.swift +// TMDbDiscoverServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class DiscoverServiceTests: XCTestCase { +final class TMDbDiscoverServiceTests: XCTestCase { - var service: DiscoverService! + var service: TMDbDiscoverService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = DiscoverService(apiClient: apiClient) + service = TMDbDiscoverService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Genres/GenreServiceTests.swift b/Tests/TMDbTests/Domain/Services/Genres/TMDbGenreServiceTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/Genres/GenreServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Genres/TMDbGenreServiceTests.swift index 27d7c714..6e0dfb83 100644 --- a/Tests/TMDbTests/Domain/Services/Genres/GenreServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Genres/TMDbGenreServiceTests.swift @@ -1,5 +1,5 @@ // -// GenreServiceTests.swift +// TMDbGenreServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class GenreServiceTests: XCTestCase { +final class TMDbGenreServiceTests: XCTestCase { - var service: GenreService! + var service: TMDbGenreService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = GenreService(apiClient: apiClient) + service = TMDbGenreService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceCreditsTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceCreditsTests.swift similarity index 92% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceCreditsTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceCreditsTests.swift index 7690df6c..4bfdd729 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceCreditsTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceCreditsTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceCreditsTests.swift +// TMDbMovieServiceCreditsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceCreditsTests: XCTestCase { +final class TMDbMovieServiceCreditsTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceDetailsTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceDetailsTests.swift similarity index 92% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceDetailsTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceDetailsTests.swift index 711de13d..66b952e9 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceDetailsTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceDetailsTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceDetailsTests.swift +// TMDbMovieServiceDetailsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceDetailsTests: XCTestCase { +final class TMDbMovieServiceDetailsTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceListsTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceListsTests.swift similarity index 98% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceListsTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceListsTests.swift index e10d2634..05b5a466 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceListsTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceListsTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceListsTests.swift +// TMDbMovieServiceListsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceListsTests: XCTestCase { +final class TMDbMovieServiceListsTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceMediaTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceMediaTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceMediaTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceMediaTests.swift index 33e99fd3..1b4d1655 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceMediaTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceMediaTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceMediaTests.swift +// TMDbMovieServiceMediaTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceMediaTests: XCTestCase { +final class TMDbMovieServiceMediaTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceOthersTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceOthersTests.swift similarity index 93% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceOthersTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceOthersTests.swift index de00492a..dd743a25 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceOthersTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceOthersTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceOthersTests.swift +// TMDbMovieServiceOthersTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceOthersTests: XCTestCase { +final class TMDbMovieServiceOthersTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceReviewsTests.swift b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceReviewsTests.swift similarity index 92% rename from Tests/TMDbTests/Domain/Services/Movies/MovieServiceReviewsTests.swift rename to Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceReviewsTests.swift index e44c296d..7c937bb0 100644 --- a/Tests/TMDbTests/Domain/Services/Movies/MovieServiceReviewsTests.swift +++ b/Tests/TMDbTests/Domain/Services/Movies/TMDbMovieServiceReviewsTests.swift @@ -1,5 +1,5 @@ // -// MovieServiceReviewsTests.swift +// TMDbMovieServiceReviewsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class MovieServiceReviewsTests: XCTestCase { +final class TMDbMovieServiceReviewsTests: XCTestCase { - var service: MovieService! + var service: TMDbMovieService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = MovieService(apiClient: apiClient) + service = TMDbMovieService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/People/PersonServiceTests.swift b/Tests/TMDbTests/Domain/Services/People/TMDbPersonServiceTests.swift similarity index 98% rename from Tests/TMDbTests/Domain/Services/People/PersonServiceTests.swift rename to Tests/TMDbTests/Domain/Services/People/TMDbPersonServiceTests.swift index c6142c96..acc2fa19 100644 --- a/Tests/TMDbTests/Domain/Services/People/PersonServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/People/TMDbPersonServiceTests.swift @@ -1,5 +1,5 @@ // -// PersonServiceTests.swift +// TMDbPersonServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class PersonServiceTests: XCTestCase { +final class TMDbPersonServiceTests: XCTestCase { - var service: PersonService! + var service: TMDbPersonService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = PersonService(apiClient: apiClient) + service = TMDbPersonService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Search/SearchServiceTests.swift b/Tests/TMDbTests/Domain/Services/Search/TMDbSearchServiceTests.swift similarity index 98% rename from Tests/TMDbTests/Domain/Services/Search/SearchServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Search/TMDbSearchServiceTests.swift index f517d8e6..005b7492 100644 --- a/Tests/TMDbTests/Domain/Services/Search/SearchServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Search/TMDbSearchServiceTests.swift @@ -1,5 +1,5 @@ // -// SearchServiceTests.swift +// TMDbSearchServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class SearchServiceTests: XCTestCase { +final class TMDbSearchServiceTests: XCTestCase { - var service: SearchService! + var service: TMDbSearchService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = SearchService(apiClient: apiClient) + service = TMDbSearchService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVEpisodes/TVEpisodeServiceTests.swift b/Tests/TMDbTests/Domain/Services/TVEpisodes/TMDbTVEpisodeServiceTests.swift similarity index 97% rename from Tests/TMDbTests/Domain/Services/TVEpisodes/TVEpisodeServiceTests.swift rename to Tests/TMDbTests/Domain/Services/TVEpisodes/TMDbTVEpisodeServiceTests.swift index 1c306525..5f700ffd 100644 --- a/Tests/TMDbTests/Domain/Services/TVEpisodes/TVEpisodeServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVEpisodes/TMDbTVEpisodeServiceTests.swift @@ -1,5 +1,5 @@ // -// TVEpisodeServiceTests.swift +// TMDbTVEpisodeServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVEpisodeServiceTests: XCTestCase { +final class TMDbTVEpisodeServiceTests: XCTestCase { - var service: TVEpisodeService! + var service: TMDbTVEpisodeService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVEpisodeService(apiClient: apiClient) + service = TMDbTVEpisodeService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeasons/TVSeasonServiceTests.swift b/Tests/TMDbTests/Domain/Services/TVSeasons/TMDbTVSeasonServiceTests.swift similarity index 97% rename from Tests/TMDbTests/Domain/Services/TVSeasons/TVSeasonServiceTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeasons/TMDbTVSeasonServiceTests.swift index e546a650..6843f088 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeasons/TVSeasonServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeasons/TMDbTVSeasonServiceTests.swift @@ -1,5 +1,5 @@ // -// TVSeasonServiceTests.swift +// TMDbTVSeasonServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeasonServiceTests: XCTestCase { +final class TMDbTVSeasonServiceTests: XCTestCase { - var service: TVSeasonService! + var service: TMDbTVSeasonService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeasonService(apiClient: apiClient) + service = TMDbTVSeasonService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceCreditsTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceCreditsTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceCreditsTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceCreditsTests.swift index 062528ca..0e9c07cc 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceCreditsTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceCreditsTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceCreditsTests.swift +// TMDbTVSeriesServiceCreditsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceCreditsTests: XCTestCase { +final class TMDbTVSeriesServiceCreditsTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceDetailsTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceDetailsTests.swift similarity index 92% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceDetailsTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceDetailsTests.swift index 9eac977f..8500eeb2 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceDetailsTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceDetailsTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceDetailsTests.swift +// TMDbTVSeriesServiceDetailsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceTests: XCTestCase { +final class TMDbTVSeriesServiceTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceListsTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceListsTests.swift similarity index 96% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceListsTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceListsTests.swift index ded8367c..9941ffc7 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceListsTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceListsTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceListsTests.swift +// TMDbTVSeriesServiceListsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceListsTests: XCTestCase { +final class TMDbTVSeriesServiceListsTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceMediaTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceMediaTests.swift similarity index 95% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceMediaTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceMediaTests.swift index a43d4fef..57b7d84b 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceMediaTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceMediaTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceMediaTests.swift +// TMDbTVSeriesServiceMediaTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceMediaTests: XCTestCase { +final class TMDbTVSeriesServiceMediaTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceOthersTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceOthersTests.swift similarity index 93% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceOthersTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceOthersTests.swift index ef715863..2ceb22ca 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceOthersTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceOthersTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceOthersTests.swift +// TMDbTVSeriesServiceOthersTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceOthersTests: XCTestCase { +final class TMDbTVSeriesServiceOthersTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceReviewsTests.swift b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceReviewsTests.swift similarity index 92% rename from Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceReviewsTests.swift rename to Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceReviewsTests.swift index 6cbf510e..b24537d4 100644 --- a/Tests/TMDbTests/Domain/Services/TVSeries/TVSeriesServiceReviewsTests.swift +++ b/Tests/TMDbTests/Domain/Services/TVSeries/TMDbTVSeriesServiceReviewsTests.swift @@ -1,5 +1,5 @@ // -// TVSeriesServiceReviewsTests.swift +// TMDbTVSeriesServiceReviewsTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TVSeriesServiceReviewsTests: XCTestCase { +final class TMDbTVSeriesServiceReviewsTests: XCTestCase { - var service: TVSeriesService! + var service: TMDbTVSeriesService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TVSeriesService(apiClient: apiClient) + service = TMDbTVSeriesService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/Trending/TrendingTimeWindowFilterTypeTests.swift b/Tests/TMDbTests/Domain/Services/Trending/Filters/TrendingTimeWindowFilterTypeTests.swift similarity index 100% rename from Tests/TMDbTests/Domain/Services/Trending/TrendingTimeWindowFilterTypeTests.swift rename to Tests/TMDbTests/Domain/Services/Trending/Filters/TrendingTimeWindowFilterTypeTests.swift diff --git a/Tests/TMDbTests/Domain/Services/Trending/TrendingServiceTests.swift b/Tests/TMDbTests/Domain/Services/Trending/TMDbTrendingServiceTests.swift similarity index 96% rename from Tests/TMDbTests/Domain/Services/Trending/TrendingServiceTests.swift rename to Tests/TMDbTests/Domain/Services/Trending/TMDbTrendingServiceTests.swift index 99139727..9bfb337e 100644 --- a/Tests/TMDbTests/Domain/Services/Trending/TrendingServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/Trending/TMDbTrendingServiceTests.swift @@ -1,5 +1,5 @@ // -// TrendingServiceTests.swift +// TMDbTrendingServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class TrendingServiceTests: XCTestCase { +final class TMDbTrendingServiceTests: XCTestCase { - var service: TrendingService! + var service: TMDbTrendingService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = TrendingService(apiClient: apiClient) + service = TMDbTrendingService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Domain/Services/WatchProviders/WatchProviderServiceTests.swift b/Tests/TMDbTests/Domain/Services/WatchProviders/TMDbWatchProviderServiceTests.swift similarity index 96% rename from Tests/TMDbTests/Domain/Services/WatchProviders/WatchProviderServiceTests.swift rename to Tests/TMDbTests/Domain/Services/WatchProviders/TMDbWatchProviderServiceTests.swift index a79d5ddb..a1ecd9ac 100644 --- a/Tests/TMDbTests/Domain/Services/WatchProviders/WatchProviderServiceTests.swift +++ b/Tests/TMDbTests/Domain/Services/WatchProviders/TMDbWatchProviderServiceTests.swift @@ -1,5 +1,5 @@ // -// WatchProviderServiceTests.swift +// TMDbWatchProviderServiceTests.swift // TMDb // // Copyright © 2024 Adam Young. @@ -20,15 +20,15 @@ @testable import TMDb import XCTest -final class WatchProviderServiceTests: XCTestCase { +final class TMDbWatchProviderServiceTests: XCTestCase { - var service: WatchProviderService! + var service: TMDbWatchProviderService! var apiClient: MockAPIClient! override func setUp() { super.setUp() apiClient = MockAPIClient() - service = WatchProviderService(apiClient: apiClient) + service = TMDbWatchProviderService(apiClient: apiClient) } override func tearDown() { diff --git a/Tests/TMDbTests/Networking/HTTPClient/MockURLProtocol.swift b/Tests/TMDbTests/Networking/HTTPClient/MockURLProtocol.swift index 683396ce..5da8cde6 100644 --- a/Tests/TMDbTests/Networking/HTTPClient/MockURLProtocol.swift +++ b/Tests/TMDbTests/Networking/HTTPClient/MockURLProtocol.swift @@ -29,11 +29,11 @@ final class MockURLProtocol: URLProtocol, @unchecked Sendable { @MainActor static var responseStatusCode: Int? @MainActor private(set) static var lastRequest: URLRequest? - override class func canInit(with _: URLRequest) -> Bool { + override static func canInit(with _: URLRequest) -> Bool { true } - override class func canonicalRequest(for request: URLRequest) -> URLRequest { + override static func canonicalRequest(for request: URLRequest) -> URLRequest { Task { await MainActor.run { lastRequest = request