From 4359f53a196a5b10abb68439da9001b3a7c9ee80 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sun, 17 Mar 2024 15:15:44 +0000 Subject: [PATCH] [Library] Feed library (#4) This PR contains the work done to setup the library and also, the necessary protocols, model, structs, and error definitions to implement remote service clients. Reviewed-on: https://repo.rock-n-code.com/rock-n-code/app-reviews/pulls/4 Co-authored-by: Javier Cicchelli Co-committed-by: Javier Cicchelli --- Frameworks/Feed/{ => Bundle}/ReviewsFeed.h | 0 .../Sources/DetailsViewController.swift | 0 .../Sources/FeedViewController.swift | 0 .../Feed/{ => Bundle}/Sources/Review.swift | 0 .../{ => Bundle}/Sources/ReviewCell.swift | 0 .../Kit/Sources/Errors/EndpointError.swift | 13 ++++++ .../Feed/Kit/Sources/Models/Review.swift | 41 +++++++++++++++++ .../Feed/Kit/Sources/Protocols/Endpoint.swift | 32 ++++++++++++++ .../Endpoints/GetReviewsEndpoint.swift | 44 +++++++++++++++++++ .../Feed/Kit/Sources/Protocols/Service.swift | 21 +++++++++ .../Structs/ServiceConfiguration.swift | 29 ++++++++++++ .../Sources/Extensions/String+Constants.swift | 2 +- Libraries/Package.swift | 39 ++++++++++++++-- Reviews.xcodeproj/project.pbxproj | 30 ++++++++++--- .../Libraries.xctestplan | 0 15 files changed, 240 insertions(+), 11 deletions(-) rename Frameworks/Feed/{ => Bundle}/ReviewsFeed.h (100%) rename Frameworks/Feed/{ => Bundle}/Sources/DetailsViewController.swift (100%) rename Frameworks/Feed/{ => Bundle}/Sources/FeedViewController.swift (100%) rename Frameworks/Feed/{ => Bundle}/Sources/Review.swift (100%) rename Frameworks/Feed/{ => Bundle}/Sources/ReviewCell.swift (100%) create mode 100644 Libraries/Feed/Kit/Sources/Errors/EndpointError.swift create mode 100644 Libraries/Feed/Kit/Sources/Models/Review.swift create mode 100644 Libraries/Feed/Kit/Sources/Protocols/Endpoint.swift create mode 100644 Libraries/Feed/Kit/Sources/Protocols/Endpoints/GetReviewsEndpoint.swift create mode 100644 Libraries/Feed/Kit/Sources/Protocols/Service.swift create mode 100644 Libraries/Feed/Kit/Sources/Structs/ServiceConfiguration.swift rename Libraries.xctestplan => Test Plans/Libraries.xctestplan (100%) diff --git a/Frameworks/Feed/ReviewsFeed.h b/Frameworks/Feed/Bundle/ReviewsFeed.h similarity index 100% rename from Frameworks/Feed/ReviewsFeed.h rename to Frameworks/Feed/Bundle/ReviewsFeed.h diff --git a/Frameworks/Feed/Sources/DetailsViewController.swift b/Frameworks/Feed/Bundle/Sources/DetailsViewController.swift similarity index 100% rename from Frameworks/Feed/Sources/DetailsViewController.swift rename to Frameworks/Feed/Bundle/Sources/DetailsViewController.swift diff --git a/Frameworks/Feed/Sources/FeedViewController.swift b/Frameworks/Feed/Bundle/Sources/FeedViewController.swift similarity index 100% rename from Frameworks/Feed/Sources/FeedViewController.swift rename to Frameworks/Feed/Bundle/Sources/FeedViewController.swift diff --git a/Frameworks/Feed/Sources/Review.swift b/Frameworks/Feed/Bundle/Sources/Review.swift similarity index 100% rename from Frameworks/Feed/Sources/Review.swift rename to Frameworks/Feed/Bundle/Sources/Review.swift diff --git a/Frameworks/Feed/Sources/ReviewCell.swift b/Frameworks/Feed/Bundle/Sources/ReviewCell.swift similarity index 100% rename from Frameworks/Feed/Sources/ReviewCell.swift rename to Frameworks/Feed/Bundle/Sources/ReviewCell.swift diff --git a/Libraries/Feed/Kit/Sources/Errors/EndpointError.swift b/Libraries/Feed/Kit/Sources/Errors/EndpointError.swift new file mode 100644 index 0000000..d8e4249 --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Errors/EndpointError.swift @@ -0,0 +1,13 @@ +// +// EndpointError.swift +// ReviewsFeedKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +public enum EndpointError: Error { + case inputParametersEmpty + case requestFailed(statusCode: Int) + case responseNotFound +} diff --git a/Libraries/Feed/Kit/Sources/Models/Review.swift b/Libraries/Feed/Kit/Sources/Models/Review.swift new file mode 100644 index 0000000..90b511e --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Models/Review.swift @@ -0,0 +1,41 @@ +// +// Review.swift +// ReviewsFeedKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public struct Review { + + // MARK: Constants + public let author: String + public let content: String + public let id: Int + public let rating: Int + public let title: String + public let updated: Date + public let version: String + + // MARK: Initialisers + public init( + id: Int, + author: String, + title: String, + content: String, + rating: Int, + version: String, + updated: Date + ) { + self.author = author + self.content = content + self.id = id + self.rating = rating + self.title = title + self.updated = updated + self.version = version + } + +} diff --git a/Libraries/Feed/Kit/Sources/Protocols/Endpoint.swift b/Libraries/Feed/Kit/Sources/Protocols/Endpoint.swift new file mode 100644 index 0000000..44f59f2 --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Protocols/Endpoint.swift @@ -0,0 +1,32 @@ +// +// Endpoint.swift +// ReviewsFeedKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public protocol Endpoint { + + // MARK: Associated types + associatedtype Input: EndpointInput + associatedtype Output: EndpointOutput + + // MARK: Properties + var host: URL { get } + var decoder: JSONDecoder { get } + var session: URLSession { get } + + // MARK: Functions + func callAsFunction(_ input: Input) async throws -> Output + func makePath(with input: Input) throws -> String + +} + +// MARK: - Input +public protocol EndpointInput {} + +// MARK: - Output +public protocol EndpointOutput {} diff --git a/Libraries/Feed/Kit/Sources/Protocols/Endpoints/GetReviewsEndpoint.swift b/Libraries/Feed/Kit/Sources/Protocols/Endpoints/GetReviewsEndpoint.swift new file mode 100644 index 0000000..0159e5e --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Protocols/Endpoints/GetReviewsEndpoint.swift @@ -0,0 +1,44 @@ +// +// GetReviewsEndpoint.swift +// ReviewsFeedKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public protocol GetReviewsEndpoint: Endpoint +where Input == GetReviewsInput, + Output == GetReviewsOutput {} + +// MARK: - Input +public struct GetReviewsInput: EndpointInput { + + // MARK: Constants + public let appID: String + public let countryCode: String + + // MARK: Initialisers + public init( + appID: String, + countryCode: String + ) { + self.appID = appID + self.countryCode = countryCode + } + +} + +// MARK: - Output +public struct GetReviewsOutput: EndpointOutput { + + // MARK: Constants + public let reviews: [Review] + + // MARK: Initialisers + public init(reviews: [Review]) { + self.reviews = reviews + } + +} diff --git a/Libraries/Feed/Kit/Sources/Protocols/Service.swift b/Libraries/Feed/Kit/Sources/Protocols/Service.swift new file mode 100644 index 0000000..d602442 --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Protocols/Service.swift @@ -0,0 +1,21 @@ +// +// Service.swift +// ReviewsFeedKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public protocol Service { + + // MARK: Properties + var configuration: ServiceConfiguration { get } + var decoder: JSONDecoder { get } + var session: URLSession { get } + + // MARK: Functions + func getReviews(_ input: GetReviewsInput) async throws -> GetReviewsOutput + +} diff --git a/Libraries/Feed/Kit/Sources/Structs/ServiceConfiguration.swift b/Libraries/Feed/Kit/Sources/Structs/ServiceConfiguration.swift new file mode 100644 index 0000000..32df28f --- /dev/null +++ b/Libraries/Feed/Kit/Sources/Structs/ServiceConfiguration.swift @@ -0,0 +1,29 @@ +// +// ServiceConfiguration.swift +// ReviewsiTunesKit +// +// Created by Javier Cicchelli on 17/03/2024. +// Copyright © 2024 Röck+Cöde VoF. All rights reserved. +// + +import Foundation + +public struct ServiceConfiguration { + + // MARK: Constants + public let decoder: JSONDecoder + public let host: URL + public let session: URLSessionConfiguration + + // MARK: Initialisers + public init( + host: URL, + session: URLSessionConfiguration = .ephemeral, + decoder: JSONDecoder? = nil + ) { + self.decoder = decoder ?? .init() + self.host = host + self.session = session + } + +} diff --git a/Libraries/Foundation/Kit/Sources/Extensions/String+Constants.swift b/Libraries/Foundation/Kit/Sources/Extensions/String+Constants.swift index ab80392..f6d84ec 100644 --- a/Libraries/Foundation/Kit/Sources/Extensions/String+Constants.swift +++ b/Libraries/Foundation/Kit/Sources/Extensions/String+Constants.swift @@ -1,6 +1,6 @@ // // String+Constants.swift -// ReviewsFoundation +// ReviewsFoundationKit // // Created by Javier Cicchelli on 16/03/2024. // Copyright © 2024 Röck+Cöde VoF. All rights reserved. diff --git a/Libraries/Package.swift b/Libraries/Package.swift index 2fa99db..25f97f7 100644 --- a/Libraries/Package.swift +++ b/Libraries/Package.swift @@ -11,22 +11,53 @@ let package = Package( .library( name: .Product.name.kit, targets: [ - .Target.foundation + .Target.feed.kit, + .Target.foundation.kit, + .Target.iTunes.kit, ] ), ], targets: [ .target( - name: .Target.foundation, + name: .Target.feed.kit, + dependencies: [ + .byName(name: .Target.foundation.kit), + ], + path: "Feed/Kit" + ), + .target( + name: .Target.foundation.kit, path: "Foundation/Kit" ), + .target( + name: .Target.iTunes.kit, + dependencies: [ + .byName(name: .Target.feed.kit), + .byName(name: .Target.foundation.kit), + ], + path: "iTunes/Kit" + ), + .testTarget( + name: .Target.feed.test, + dependencies: [ + .byName(name: .Target.feed.kit), + ], + path: "Feed/Test" + ), .testTarget( name: .Target.foundation.test, dependencies: [ - .byName(name: .Target.foundation) + .byName(name: .Target.foundation.kit), ], path: "Foundation/Test" ), + .testTarget( + name: .Target.iTunes.test, + dependencies: [ + .byName(name: .Target.iTunes.kit), + ], + path: "iTunes/Test" + ), ] ) @@ -41,7 +72,9 @@ private extension String { } enum Target { + static let feed = "\(String.Product.name)Feed" static let foundation = "\(String.Product.name)Foundation" + static let iTunes = "\(String.Product.name)iTunes" } } diff --git a/Reviews.xcodeproj/project.pbxproj b/Reviews.xcodeproj/project.pbxproj index 4feecf0..22420ed 100644 --- a/Reviews.xcodeproj/project.pbxproj +++ b/Reviews.xcodeproj/project.pbxproj @@ -46,7 +46,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 02900C492BA530E6008D2E8D /* Libraries.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Libraries.xctestplan; sourceTree = ""; }; + 02900C4B2BA5347A008D2E8D /* Libraries.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Libraries.xctestplan; sourceTree = ""; }; 02DC7F8F2BA51793000EEEBE /* ReviewsFeed.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReviewsFeed.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02DC7F912BA51793000EEEBE /* ReviewsFeed.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReviewsFeed.h; sourceTree = ""; }; 02DC7FB12BA52084000EEEBE /* Libraries */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Libraries; sourceTree = ""; }; @@ -82,12 +82,28 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 02900C4A2BA53162008D2E8D /* Test Plans */ = { + 02900C4C2BA5347A008D2E8D /* Test Plans */ = { isa = PBXGroup; children = ( - 02900C492BA530E6008D2E8D /* Libraries.xctestplan */, + 02900C4B2BA5347A008D2E8D /* Libraries.xctestplan */, ); - name = "Test Plans"; + path = "Test Plans"; + sourceTree = ""; + }; + 02A6DA2F2BA591C000B943E2 /* Bundle */ = { + isa = PBXGroup; + children = ( + 02DC7F912BA51793000EEEBE /* ReviewsFeed.h */, + 02DC7FB02BA51B4F000EEEBE /* Sources */, + ); + path = Bundle; + sourceTree = ""; + }; + 02A6DA302BA5929F00B943E2 /* Test */ = { + isa = PBXGroup; + children = ( + ); + path = Test; sourceTree = ""; }; 02DC7F722BA4F8F0000EEEBE /* Resources */ = { @@ -135,8 +151,8 @@ 02DC7F902BA51793000EEEBE /* Feed */ = { isa = PBXGroup; children = ( - 02DC7F912BA51793000EEEBE /* ReviewsFeed.h */, - 02DC7FB02BA51B4F000EEEBE /* Sources */, + 02A6DA2F2BA591C000B943E2 /* Bundle */, + 02A6DA302BA5929F00B943E2 /* Test */, ); path = Feed; sourceTree = ""; @@ -166,7 +182,7 @@ 02DC7FB12BA52084000EEEBE /* Libraries */, 02DC7FAB2BA51848000EEEBE /* Frameworks */, 345AD11A24C6EDD9004E2EE1 /* App */, - 02900C4A2BA53162008D2E8D /* Test Plans */, + 02900C4C2BA5347A008D2E8D /* Test Plans */, 345AD11924C6EDD9004E2EE1 /* Products */, ); sourceTree = ""; diff --git a/Libraries.xctestplan b/Test Plans/Libraries.xctestplan similarity index 100% rename from Libraries.xctestplan rename to Test Plans/Libraries.xctestplan