Swiftpack.co - Package - Flinesoft/Microya

Build Status codebeat badge Version: 0.2.0 Swift: 5.3 Platforms: iOS | macOS | tvOS | watchOS License: MIT
PayPal: Donate GitHub: Become a sponsor Patreon: Become a patron

InstallationUsageDonationIssuesContributingLicense

Microya

A micro version of the Moya network abstraction layer written in Swift.

Installation

Installation is only supported via SwiftPM.

Usage

Step 1: Defining your Endpoints

Create an Api enum with all supported endpoints as cases with the request parameters/data specified as parameters.

For example, when writing a client for the Microsoft Translator API:

enum MicrosoftTranslatorApi {
    case languages
    case translate(texts: [String], from: Language, to: [Language])
}

Note that the Language type used above does not necessarily need to be an Encodable type:

enum Language: String {
    case english = "en"
    case german = "de"
    case japanese = "jp"
    case turkish = "tr"
}

Step 2: Making your Api JsonApi compliant

Add an extension for your Api enum that makes it JsonApi compliant, which means you need to add implementations for the following protocol:

protocol JsonApi {
    var decoder: JSONDecoder { get }
    var encoder: JSONEncoder { get }

    var baseUrl: URL { get }
    var headers: [String: String] { get }
    var path: String { get }
    var method: Method { get }
    var queryParameters: [(key: String, value: String)] { get }
    var bodyData: Data? { get }
}

Use switch statements over self to differentiate between the cases (if needed) and to provide the appropriate data the protocol asks for (using Value Bindings).

Toggle me to see an example
extension MicrosoftTranslatorApi: JsonApi {
    var decoder: JSONDecoder {
        return JSONDecoder()
    }

    var encoder: JSONEncoder {
        return JSONEncoder()
    }

    var baseUrl: URL {
        return URL(string: "https://api.cognitive.microsofttranslator.com")!
    }

    var path: String {
        switch self {
        case .languages:
            return "/languages"

        case .translate:
            return "/translate"
        }
    }

    var method: Method {
        switch self {
        case .languages:
            return .get

        case .translate:
            return .post
        }
    }

    var queryParameters: [(key: String, value: String)] {
        var urlParameters: [(String, String)] = [(key: "api-version", value: "3.0")]

        switch self {
        case .languages:
            break

        case let .translate(_, sourceLanguage, targetLanguages, _):
            urlParameters.append((key: "from", value: sourceLanguage.rawValue))

            for targetLanguage in targetLanguages {
                urlParameters.append((key: "to", value: targetLanguage.rawValue))
            }              
        }

        return urlParameters
    }

    var bodyData: Data? {
        switch self {
        case .translate:
            return nil // no request data needed

        case let .translate(texts, _, _, _):
            return try! encoder.encode(texts)
        }
    }

    var headers: [String: String] {
        switch self {
        case .languages:
            return [:]

        case .translate:
            return [
                "Ocp-Apim-Subscription-Key": "<SECRET>",
                "Content-Type": "application/json"
            ]
        }
    }
}

Step 3: Calling your API endpoint with the Result type

Call an API endpoint providing a Decodable type of the expected result (if any) by using this method pre-implemented in the JsonApi protocol:

func request<ResultType: Decodable>(type: ResultType.Type) -> Result<ResultType, JsonApiError>

For example:

let endpoint = MicrosoftTranslatorApi.translate(texts: ["Test"], from: .english, to: [.german, .japanese, .turkish])

switch endpoint.request(type: [String: String].self) {
case let .success(translationsByLanguage):
    // use the already decoded `[String: String]` result

case let .failure(error):
    // error handling
}

Note that you can also use the throwing get() function of Swift 5's Result type instead of using a switch statement:

let endpoint = MicrosoftTranslatorApi.translate(texts: ["Test"], from: .english, to: [.german, .japanese, .turkish])
let translationsByLanguage = try endpoint.request(type: [String: String].self).get()
// use the already decoded `[String: String]` result

There's even useful functional methods defined on the Result type like map(), flatMap() or mapError() and flatMapError(). See the "Transforming Result" section in this article for more information.

Donation

Microya was brought to you by Cihat Gündüz in his free time. If you want to thank me and support the development of this project, please make a small donation on PayPal. In case you also like my other open source contributions and articles, please consider motivating me by becoming a sponsor on GitHub or a patron on Patreon.

Thank you very much for any donation, it really helps out a lot! 💯

Contributing

See the file CONTRIBUTING.md.

License

This library is released under the MIT License. See LICENSE for details.

Github

link
Stars: 14

Dependencies

Releases

- 2020-08-15 16:58:23

Changed

  • Make some fields of the JsonApi protocol optional by providing default implementation.

- 2019-02-14 15:21:35

Added

  • Add JsonApi type similar to TargetType in Moya with additional JSON Codable support.
  • Add basic usage documentation based on the Microsoft Translator API.