[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: #4
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit is contained in:
Javier Cicchelli 2024-03-17 15:15:44 +00:00 committed by Javier Cicchelli
parent 8fa5533426
commit 4359f53a19
15 changed files with 240 additions and 11 deletions

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 {}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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.

View File

@ -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"
}
}

View File

@ -46,7 +46,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
02900C492BA530E6008D2E8D /* Libraries.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Libraries.xctestplan; sourceTree = "<group>"; };
02900C4B2BA5347A008D2E8D /* Libraries.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Libraries.xctestplan; sourceTree = "<group>"; };
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 = "<group>"; };
02DC7FB12BA52084000EEEBE /* Libraries */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Libraries; sourceTree = "<group>"; };
@ -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 = "<group>";
};
02A6DA2F2BA591C000B943E2 /* Bundle */ = {
isa = PBXGroup;
children = (
02DC7F912BA51793000EEEBE /* ReviewsFeed.h */,
02DC7FB02BA51B4F000EEEBE /* Sources */,
);
path = Bundle;
sourceTree = "<group>";
};
02A6DA302BA5929F00B943E2 /* Test */ = {
isa = PBXGroup;
children = (
);
path = Test;
sourceTree = "<group>";
};
02DC7F722BA4F8F0000EEEBE /* Resources */ = {
@ -135,8 +151,8 @@
02DC7F902BA51793000EEEBE /* Feed */ = {
isa = PBXGroup;
children = (
02DC7F912BA51793000EEEBE /* ReviewsFeed.h */,
02DC7FB02BA51B4F000EEEBE /* Sources */,
02A6DA2F2BA591C000B943E2 /* Bundle */,
02A6DA302BA5929F00B943E2 /* Test */,
);
path = Feed;
sourceTree = "<group>";
@ -166,7 +182,7 @@
02DC7FB12BA52084000EEEBE /* Libraries */,
02DC7FAB2BA51848000EEEBE /* Frameworks */,
345AD11A24C6EDD9004E2EE1 /* App */,
02900C4A2BA53162008D2E8D /* Test Plans */,
02900C4C2BA5347A008D2E8D /* Test Plans */,
345AD11924C6EDD9004E2EE1 /* Products */,
);
sourceTree = "<group>";