From c4a25afad4bb42b25256c9d94aa3ec706d0f301b Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sun, 23 Apr 2023 13:16:22 +0000 Subject: [PATCH] [Setup] Documentation (#10) This PR contains the work done to address the issue #7, related to documenting the source code that would be used for other developers. To provide further details about the work done: - [x] restructured the hierarchy of some models that are related to the `Amiibo` model; - [x] written documentation for the `AmiiboService` service; - [x] written documentation for the `AmiiboFilter` and `KeyNameFilter` filters; - [x] written documentation for the `Amiibo`, `KeyName`, `LastUpdated` and children model; - [x] written documentation for the `AmiiboClientError` error; - [x] written documentation for the README file. Co-authored-by: Javier Cicchelli Reviewed-on: https://repo.rock-n-code.com/rock-n-code/amiibo-service/pulls/10 --- README.md | 71 +++++++++++++++++++++++++- Sources/Errors/AmiiboClientError.swift | 3 ++ Sources/Filters/AmiiboFilter.swift | 26 ++++++++-- Sources/Filters/KeyNameFilter.swift | 26 +++++++--- Sources/Models/Amiibo.swift | 35 +++++++++++++ Sources/Models/AmiiboGame.swift | 27 ++++++++++ Sources/Models/AmiiboGameUsage.swift | 23 +++++++++ Sources/Models/AmiiboRelease.swift | 33 ++++++++++++ Sources/Models/Game.swift | 15 ------ Sources/Models/KeyName.swift | 31 ++++++++--- Sources/Models/LastUpdated.swift | 6 +++ Sources/Models/Release.swift | 19 ------- Sources/Models/Usage.swift | 13 ----- Sources/Services/AmiiboService.swift | 32 +++++++++++- 14 files changed, 290 insertions(+), 70 deletions(-) create mode 100644 Sources/Models/AmiiboGame.swift create mode 100644 Sources/Models/AmiiboGameUsage.swift create mode 100644 Sources/Models/AmiiboRelease.swift delete mode 100644 Sources/Models/Game.swift delete mode 100644 Sources/Models/Release.swift delete mode 100644 Sources/Models/Usage.swift diff --git a/README.md b/README.md index ab5c1af..115e236 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,70 @@ -# AmiiboService +# AmiiboService: Amiibo API service written in Swift -A description of this package. +This package contains a ready-to-use service that can retrieve data as decoded models/entities from any of the [Amiibo API](https://www.amiiboapi.com) available endpoints. + +## Installation + +It is possible to add this library in your projects either by explicitly add it as a dependency either in a `Package.swift` file or in your Xcode project. + +This package has minimum platform requirements that are important to take into account: +* *iOS 13.0 or higher*; +* *macOS 10.15 or higher*; +* *tvOS 13.0 or higher*; +* *watchOS 8.0 or higher*; + +### Package file + +In the intended `Package.swift` file, it is required to add the following dependency: + +```swift +dependencies: [ + // ... + .package(url: "https://github.com/rock-n-code/amiibo-service.git", from: "1.0.0") + // ... +], +``` + +Then it is required to add the package to your target, like this: + +```swift +targets: [ + .target( + ... + dependencies: [ + .product(name: "AmiiboService", package: "amiibo-service") + ], + ... + ), +] +``` + +### Xcode + +In an opened Xcode project, it is required to do these steps: +1. click on your Xcode project file to display *Project* and *Targets*; +2. select the project under the *Project* section to show the *Info*, *Build Settings* and *Package Dependencies* in the center menu of the screen; +3. select the *Package Dependencies* to show the defined dependencies of the project; +4. press on the *+* (plus) button to add dependencies to the project; +5. enter the URL`https://github.com/rock-n-code/amiibo-service` into the *Search or Enter Package URL* located in the upper right corner; +6. select the given option; +7. define the dependency rule (the *Up to Next Major Version* option and the *1.0.0* text are recommended); +8. select the target to which the dependency will be applied (if required); +9. wait for the package to be resolved and included in the project; +10. done! + + +### Other considerations + +This library is fully supported on Apple platforms only for the time being: *iOS*, *macOS*, *tvOS*, and *watchOS*. Support for other platforms such as *Linux* or *Windows* might be added in the future, depending on the type of changes those platforms require but this needs to be researched first. + +⚠️ Please notice that this library only supports the [Swift Package Manager](https://www.swift.org/package-manager/), and that support for other dependency managers such as *Cocoapods* and *Carthage* is not planned. + +## Usage + +This package provides an actor called `AmiiboService` which, after is initialised, is responsible for making requests based on a given filter criteria to the remote API, handling the responses, and decoding the retrieved data into models/entities structs, or throws errors if necessary. + +It is needless to say, all the necessary filters, models/entities, and enumerations are also provided in this package. + +## Further documentation + +For further information about the API, please refer to the [Amiibo API documentation](https://www.amiiboapi.com/docs/) available online. diff --git a/Sources/Errors/AmiiboClientError.swift b/Sources/Errors/AmiiboClientError.swift index 6e49f0c..f0894fb 100644 --- a/Sources/Errors/AmiiboClientError.swift +++ b/Sources/Errors/AmiiboClientError.swift @@ -1,4 +1,7 @@ +/// This error definitions represents any error happening while the client makes a request to the remote API and handles the respective response, excluding the decoding of the retrieved data into a particular model. public enum AmiiboClientError: Error { + /// The status code of the response is not the expected one, which is `.ok` (`200`). case responseCode(Int) + /// The status code of the response was not received at all. case responseCodeNotFound } diff --git a/Sources/Filters/AmiiboFilter.swift b/Sources/Filters/AmiiboFilter.swift index 72efa07..1e9d1f8 100644 --- a/Sources/Filters/AmiiboFilter.swift +++ b/Sources/Filters/AmiiboFilter.swift @@ -1,3 +1,4 @@ +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering amiibos. public struct AmiiboFilter { // MARK: Properties @@ -14,6 +15,17 @@ public struct AmiiboFilter { // MARK: Initialisers + /// Initialises this filter. + /// - Parameters: + /// - id: An amiibo `id` value to match against. + /// - head: An amiibo `head` value to match against. + /// - tail: An amiibo `tail` value to match against. + /// - name: An amiibo `name` value to match against. + /// - type: An amiibo `type` key or name value to match against. + /// - gameSeries: An amiibo `gameSeries` key or name value to match against. + /// - amiiboSeries: An amiibo `amiiboSeries` key or name value to match against. + /// - character: An amiibo `character` key or name value to match against. + /// - showExtras: A ``ShowExtras`` enumeration that indicates whether amiibo extra information need to be retrieved or not. public init( id: String? = nil, head: String? = nil, @@ -95,10 +107,16 @@ extension AmiiboFilter: Filter { // MARK: - Enumerations -public enum ShowExtras { - case none - case games - case usage +extension AmiiboFilter { + /// This enumeration indicates if extra information for amiibos need to be retrieved. + public enum ShowExtras { + /// No extra information needs to be retrieved. + case none + /// Amiibo games information needs to be retrieved. + case games + /// Amiibo games and its usage information needs to be retrieved. + case usage + } } // MARK: - String+Key diff --git a/Sources/Filters/KeyNameFilter.swift b/Sources/Filters/KeyNameFilter.swift index 554033b..c8c09c9 100644 --- a/Sources/Filters/KeyNameFilter.swift +++ b/Sources/Filters/KeyNameFilter.swift @@ -1,3 +1,4 @@ +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering amiibos series (``AmiiboSeriesFilter``), amiibo types (``AmiiboTypeFilter``), characters (``CharacterFilter``), or game series (``GameSeriesFilter``). public struct KeyNameFilter { // MARK: Properties @@ -7,6 +8,10 @@ public struct KeyNameFilter { // MARK: Initialisers + /// Initialises this filter. + /// - Parameters: + /// - key: A `key` value to match against. + /// - name: A `name` value to match against. public init( key: String? = nil, name: String? = nil @@ -17,6 +22,20 @@ public struct KeyNameFilter { } +// MARK: - Type aliases + +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering amiibos series. +public typealias AmiiboSeriesFilter = KeyNameFilter + +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering amiibo types. +public typealias AmiiboTypeFilter = KeyNameFilter + +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering characters. +public typealias CharacterFilter = KeyNameFilter + +/// This filter provides all the possible parameters (and combinations) available at the remote API applies when filtering game series. +public typealias GameSeriesFilter = KeyNameFilter + // MARK: - Filter extension KeyNameFilter: Filter { @@ -39,13 +58,6 @@ extension KeyNameFilter: Filter { } -// MARK: - Type aliases - -public typealias AmiiboSeriesFilter = KeyNameFilter -public typealias AmiiboTypeFilter = KeyNameFilter -public typealias CharacterFilter = KeyNameFilter -public typealias GameSeriesFilter = KeyNameFilter - // MARK: - String+Key private extension String { diff --git a/Sources/Models/Amiibo.swift b/Sources/Models/Amiibo.swift index 4a7ef2b..5725f26 100644 --- a/Sources/Models/Amiibo.swift +++ b/Sources/Models/Amiibo.swift @@ -1,22 +1,54 @@ +/// This model struct represents an amiibo that is retrieved from the respective [remote API endpoint](https://www.amiiboapi.com/docs/#amiibo). public struct Amiibo { + + // MARK: Properties + + /// The type the amiibo belongs to. public let type: String + + /// The first 8 values of the hexadecimal that identifies the amiibo. public let head: String + + /// The last 8 values of the hexadecimal that identifies the amiibo. public let tail: String + + /// The name of the amiibo. public let name: String + + /// The character of the amiibo. public let character: String + + /// The series the amiibo belongs to. public let series: String + + /// The game series of the amiibo. public let gameSeries: String + + /// The URL to an image of the amiibo. public let image: String + + /// The release dates of the amiibo (if released) in Australia, Europe, Japan and North America. public let release: Release + + /// The games related to the amiibo, if requested. public let games: Games? } // MARK: - Structs extension Amiibo { + /// This model represents the list of games related to a particular amiibo, grouped by system. public struct Games: Decodable { + + // MARK: Properties + + /// A list of [Nintendo 3DS system](https://en.wikipedia.org/wiki/Nintendo_3DS) games the amiibo can be used with. public let n3ds: [Game] + + /// A list of [Nintendo WiiU system](https://en.wikipedia.org/wiki/Wii_U) games the amiibo can be used with. public let wiiu: [Game] + + /// /// A list of [Nintendo Switch system](https://en.wikipedia.org/wiki/Nintendo_Switch) games the amiibo can be used with. public let `switch`: [Game] } } @@ -44,6 +76,9 @@ extension Amiibo: Decodable { // MARK: Initialisers + /// Initialises this model by decoding from the given decoder. + /// - Parameter decoder: The decoder to read data from. + /// - Throws: A `DecodingError` error in case the decode failed at decoding data into an expected model type. public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let games3ds = try container.decodeIfPresent([Game].self, forKey: .games3DS) diff --git a/Sources/Models/AmiiboGame.swift b/Sources/Models/AmiiboGame.swift new file mode 100644 index 0000000..377a0df --- /dev/null +++ b/Sources/Models/AmiiboGame.swift @@ -0,0 +1,27 @@ +extension Amiibo { + /// This model structs represents a game that is related to an amiibo, when requested to the respective [remote API endpoint](https://www.amiiboapi.com/docs/#showGames). + public struct Game { + + // MARK: Properties + + /// The list of identifiers associated to the game. + public let ids: [String] + + /// The name of the game. + public let name: String + + /// The list of usages that explains how the amiibo is being used in the game. + public let usage: [Usage]? + + } +} + +// MARK: - Decodable + +extension Amiibo.Game: Decodable { + enum CodingKeys: String, CodingKey { + case ids = "gameID" + case name = "gameName" + case usage = "amiiboUsage" + } +} diff --git a/Sources/Models/AmiiboGameUsage.swift b/Sources/Models/AmiiboGameUsage.swift new file mode 100644 index 0000000..05ca396 --- /dev/null +++ b/Sources/Models/AmiiboGameUsage.swift @@ -0,0 +1,23 @@ +extension Amiibo.Game { + /// This model struct represents how an amiibo is used with a particular game, when requested to the respective [remote API endpoint](https://www.amiiboapi.com/docs/#showUsage). + public struct Usage { + + // MARK: Properties + + /// The explanation on how an amiibo is being used with a particular game. + public let explanation: String + + /// A flag that indicates whether an amiibo is only read-only or the game can also write information to the amiibo. + public let isWritable: Bool + + } +} + +// MARK: - Decodable + +extension Amiibo.Game.Usage: Decodable { + enum CodingKeys: String, CodingKey { + case explanation = "Usage" + case isWritable = "write" + } +} diff --git a/Sources/Models/AmiiboRelease.swift b/Sources/Models/AmiiboRelease.swift new file mode 100644 index 0000000..fc78b2e --- /dev/null +++ b/Sources/Models/AmiiboRelease.swift @@ -0,0 +1,33 @@ +import Foundation + +extension Amiibo { + /// This model struct represents a collection of official release dates (if released) of an amiibo in different markets around the world. + public struct Release { + + // MARK: Properties + + /// The official release date (if released) of an amiibo in Australia. + public let australia: Date? + + /// The official release date (if released) of an amiibo in Europe. + public let europe: Date? + + /// The official release date (if released) of an amiibo in Japan. + public let japan: Date? + + /// The official release date (if released) of an amiibo in North America. + public let america: Date? + + } +} + +// MARK: - Decodable + +extension Amiibo.Release: Decodable { + enum CodingKeys: String, CodingKey { + case australia = "au" + case europe = "eu" + case japan = "jp" + case america = "na" + } +} diff --git a/Sources/Models/Game.swift b/Sources/Models/Game.swift deleted file mode 100644 index 304267d..0000000 --- a/Sources/Models/Game.swift +++ /dev/null @@ -1,15 +0,0 @@ -public struct Game { - public let ids: [String] - public let name: String - public let usage: [Usage]? -} - -// MARK: - Decodable - -extension Game: Decodable { - enum CodingKeys: String, CodingKey { - case ids = "gameID" - case name = "gameName" - case usage = "amiiboUsage" - } -} diff --git a/Sources/Models/KeyName.swift b/Sources/Models/KeyName.swift index f606206..228ad88 100644 --- a/Sources/Models/KeyName.swift +++ b/Sources/Models/KeyName.swift @@ -1,15 +1,30 @@ +/// This model is a concrete genetic definition that represents the following models: ``AmiiboSeries``, ``AmiiboType``, ``Character`` and ``GameSeries``. public struct KeyName { + + // MARK: Properties + + /// The key of the model. public let key: String - public let name: String? + + /// The name of the model. + public let name: String + } +// MARK: - Type aliases + +/// This model represents the series an amiibo belongs to. +public typealias AmiiboSeries = KeyName + +/// This model represents the type an amiibo belongs to. +public typealias AmiiboType = KeyName + +/// This model represents the character an amiibo is associated to. +public typealias Character = KeyName + +/// This model represents the games series an amiibo is associated to. +public typealias GameSeries = KeyName + // MARK: - Decodable extension KeyName: Decodable {} - -// MARK: - Type aliases - -public typealias AmiiboType = KeyName -public typealias AmiiboSeries = KeyName -public typealias GameSeries = KeyName -public typealias Character = KeyName diff --git a/Sources/Models/LastUpdated.swift b/Sources/Models/LastUpdated.swift index 0fefe45..1b78c00 100644 --- a/Sources/Models/LastUpdated.swift +++ b/Sources/Models/LastUpdated.swift @@ -1,7 +1,13 @@ import Foundation +/// This model represents the latest date when the remote API has been updated. public struct LastUpdated { + + // MARK: Properties + + /// The date of the latest update of the remote API. public let timestamp: Date + } // MARK: - Decodable diff --git a/Sources/Models/Release.swift b/Sources/Models/Release.swift deleted file mode 100644 index b3b672e..0000000 --- a/Sources/Models/Release.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation - -public struct Release { - public let australia: Date? - public let europe: Date? - public let japan: Date? - public let america: Date? -} - -// MARK: - Decodable - -extension Release: Decodable { - enum CodingKeys: String, CodingKey { - case australia = "au" - case europe = "eu" - case japan = "jp" - case america = "na" - } -} diff --git a/Sources/Models/Usage.swift b/Sources/Models/Usage.swift deleted file mode 100644 index 7d54439..0000000 --- a/Sources/Models/Usage.swift +++ /dev/null @@ -1,13 +0,0 @@ -public struct Usage { - public let explanation: String - public let isWritable: Bool -} - -// MARK: - Decodable - -extension Usage: Decodable { - enum CodingKeys: String, CodingKey { - case explanation = "Usage" - case isWritable = "write" - } -} diff --git a/Sources/Services/AmiiboService.swift b/Sources/Services/AmiiboService.swift index de83f3b..79b7fe5 100644 --- a/Sources/Services/AmiiboService.swift +++ b/Sources/Services/AmiiboService.swift @@ -1,6 +1,9 @@ import Foundation -public struct AmiiboService { +/// This service provides the interface to make remote API calls to the [Amiibo API](https://www.amiiboapi.com) and, subsequently, handle its responses. +/// +/// Given that this remote service is a read-only API, this service will exclusively return decoded models or entities in cases the requests are successful, or it will throw errors otherwise. +public actor AmiiboService { // MARK: Properties @@ -8,7 +11,9 @@ public struct AmiiboService { // MARK: Initialisers - init(configuration: URLSessionConfiguration) { + /// Initialises this service. + /// - Parameter configuration: The `URLSessionConfiguration` configuration to use to set this service. + public init(configuration: URLSessionConfiguration) { self.client = .init(configuration: configuration) } @@ -20,6 +25,10 @@ extension AmiiboService: Service { // MARK: Functions + /// Retrieves a list of amiibos from a remote location that matches a given filter criteria. + /// - Parameter filter: A ``AmiiboFilter`` instance that contains the filtering criteria. + /// - Returns: A list of decoded ``Amiibo`` instances that matches the given filter criteria. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func amiibos( filter: AmiiboFilter = .init() ) async throws -> [Amiibo] { @@ -31,6 +40,10 @@ extension AmiiboService: Service { ).items } + /// Retrieves a list of amiibo series from a remote location that matches a given filter criteria. + /// - Parameter filter: A ``AmiiboSeriesFilter`` instance that contains the filtering criteria. + /// - Returns: A list of decoded ``AmiiboSeries`` instances that matches the given filter criteria. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func amiiboSeries( filter: AmiiboSeriesFilter = .init() ) async throws -> [AmiiboSeries] { @@ -42,6 +55,10 @@ extension AmiiboService: Service { ).items } + /// Retrieves a list of amiibo types from a remote location that matches a given filter criteria. + /// - Parameter filter: A ``AmiiboTypeFilter`` instance that contains the filtering criteria. + /// - Returns: A list of decoded ``AmiiboType`` instances that matches the given filter criteria. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func amiiboTypes( filter: AmiiboTypeFilter = .init() ) async throws -> [AmiiboType] { @@ -53,6 +70,10 @@ extension AmiiboService: Service { ).items } + /// Retrieves a list of characters from a remote location that matches a given filter criteria. + /// - Parameter filter: A ``CharacterFilter`` instance that contains the filtering criteria. + /// - Returns: A list of decoded ``Character`` instances that matches the given filter criteria. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func characters( filter: CharacterFilter = .init() ) async throws -> [Character] { @@ -64,6 +85,10 @@ extension AmiiboService: Service { ).items } + /// Retrieves a list of game series from a remote location that matches a given filter criteria. + /// - Parameter filter: A ``GameSeriesFilter`` instance that contains the filtering criteria. + /// - Returns: A list of decoded ``GameSeries`` instances that matches the given filter criteria. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func gameSeries( filter: GameSeriesFilter = .init() ) async throws -> [GameSeries] { @@ -75,6 +100,9 @@ extension AmiiboService: Service { ).items } + /// Retrieves the date in which the remote API was last updated. + /// - Returns: A `Date` instance that represents the date in which the remote API was last updated. + /// - Throws: A ``AmiiboClientError`` is thrown in case a request failed, or a `DecodingError` is thrown in case the decoding of some object failed. public func lastUpdated() async throws -> Date { client.setDateDecodingStrategy(.formatted(.dateAndTime))