{"id":29047272,"url":"https://github.com/flinedev/microya","last_synced_at":"2025-06-26T17:05:52.084Z","repository":{"id":46681570,"uuid":"170699160","full_name":"FlineDev/Microya","owner":"FlineDev","description":"Micro version of the Moya network abstraction layer written in Swift.","archived":true,"fork":false,"pushed_at":"2023-11-29T01:10:28.000Z","size":270,"stargazers_count":46,"open_issues_count":2,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-14T13:05:38.845Z","etag":null,"topics":["api","codable","json-api","moya","networking","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FlineDev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-02-14T13:54:12.000Z","updated_at":"2025-04-21T21:17:13.000Z","dependencies_parsed_at":"2025-06-26T17:02:04.308Z","dependency_job_id":null,"html_url":"https://github.com/FlineDev/Microya","commit_stats":{"total_commits":88,"total_committers":3,"mean_commits":"29.333333333333332","dds":"0.045454545454545414","last_synced_commit":"0989427f6d1cd16fba5172ca8ff7a1ad34663e5a"},"previous_names":["flinesoft/microya"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/FlineDev/Microya","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlineDev%2FMicroya","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlineDev%2FMicroya/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlineDev%2FMicroya/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlineDev%2FMicroya/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FlineDev","download_url":"https://codeload.github.com/FlineDev/Microya/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FlineDev%2FMicroya/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262109245,"owners_count":23260318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","codable","json-api","moya","networking","swift"],"created_at":"2025-06-26T17:01:44.764Z","updated_at":"2025-06-26T17:05:52.064Z","avatar_url":"https://github.com/FlineDev.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/FlineDev/Microya/main/Logo.png\"\n      width=396\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/FlineDev/Microya/actions?query=workflow%3ACI+branch%3Amain\"\u003e\n        \u003cimg src=\"https://github.com/FlineDev/Microya/workflows/CI/badge.svg?branch=main\"\n             alt=\"CI Status\"\u003e\n    \u003c/a\u003e    \n    \u003ca href=\"https://codebeat.co/projects/github-com-flinesoft-microya-main\"\u003e\n        \u003cimg src=\"https://codebeat.co/badges/a669e100-d30d-4801-b72d-3625ab7240be\"\n             alt=\"codebeat badge\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/FlineDev/HandySwift/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Version-0.7.0-blue.svg\"\n         alt=\"Version: 0.7.0\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5.4-FFAC45.svg\"\n         alt=\"Swift: 5.4\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Platforms-Apple%20%7C%20Linux-FF69B4.svg\"\n        alt=\"Platforms: Apple | Linux\"\u003e\n    \u003ca href=\"https://github.com/FlineDev/Microya/blob/main/LICENSE.md\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-MIT-lightgrey.svg\"\n              alt=\"License: MIT\"\u003e\n    \u003c/a\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://paypal.me/Dschee/5EUR\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/PayPal-Donate-orange.svg\"\n             alt=\"PayPal: Donate\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/sponsors/Jeehut\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/GitHub-Become a sponsor-orange.svg\"\n             alt=\"GitHub: Become a sponsor\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://patreon.com/Jeehut\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Patreon-Become a patron-orange.svg\"\n             alt=\"Patreon: Become a patron\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n  • \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n  • \u003ca href=\"#donation\"\u003eDonation\u003c/a\u003e\n  • \u003ca href=\"https://github.com/FlineDev/Microya/issues\"\u003eIssues\u003c/a\u003e\n  • \u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\n  • \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\n# Microya\n\nA micro version of the [Moya](https://github.com/Moya/Moya) network abstraction layer written in Swift.\n\n## Installation\n\nInstallation is only supported via [SwiftPM](https://github.com/apple/swift-package-manager).\n\n\u003e :warning: If you need to support platform where the `Combine` framework is not available (\u003c iOS/tvOS 13, \u003c macOS 10.15), please use the `support/without-combine` branch instead.\n\n## Usage\n\n### Step 1: Defining your Endpoints\nCreate an Api `enum` with all supported endpoints as `cases` with the request parameters/data specified as parameters.\n\nFor example, when writing a client for the [Microsoft Translator API](https://docs.microsoft.com/en-us/azure/cognitive-services/translator/reference/v3-0-languages):\n\n```Swift\nenum MicrosoftTranslatorApi {\n    case languages\n    case translate(texts: [String], from: Language, to: [Language])\n}\n```\n\n### Step 2: Making your Api `Endpoint` compliant\n\nAdd an extension for your Api `enum` that makes it `Endpoint` compliant, which means you need to add implementations for the following protocol:\n\n```Swift\npublic protocol Endpoint {\n    associatedtype ClientErrorType: Decodable\n    var decoder: JSONDecoder { get }\n    var encoder: JSONEncoder { get }\n    var baseUrl: URL { get }\n    var headers: [String: String] { get }\n    var subpath: String { get }\n    var method: HttpMethod { get }\n    var queryParameters: [String: QueryParameterValue] { get }\n    var mockedResponse: MockedResponse? { get }\n}\n```\n\nUse `switch` statements over `self` to differentiate between the cases (if needed) and to provide the appropriate data the protocol asks for (using [Value Bindings](https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html#ID133)).\n\n\u003cdetails\u003e\n\u003csummary\u003eToggle me to see an example\u003c/summary\u003e\n\n```Swift\nextension MicrosoftTranslatorEndpoint: Endpoint {\n    typealias ClientErrorType = EmptyResponseType\n\n    var decoder: JSONDecoder {\n        return JSONDecoder()\n    }\n\n    var encoder: JSONEncoder {\n        return JSONEncoder()\n    }\n\n    var baseUrl: URL {\n        return URL(string: \"https://api.cognitive.microsofttranslator.com\")!\n    }\n\n    var headers: [String: String] {\n        switch self {\n        case .languages:\n            return [:]\n\n        case .translate:\n            return [\n                \"Ocp-Apim-Subscription-Key\": \"\u003cSECRET\u003e\",\n                \"Content-Type\": \"application/json\"\n            ]\n        }\n    }\n\n    var subpath: String {\n        switch self {\n        case .languages:\n            return \"/languages\"\n\n        case .translate:\n            return \"/translate\"\n        }\n    }\n\n    var method: HttpMethod {\n        switch self {\n        case .languages:\n            return .get\n\n        case let .translate(texts, _, _, _):\n            return .post(try! encoder.encode(texts))\n        }\n    }\n\n    var queryParameters: [String: QueryParameterValue] {\n        var queryParameters: [String: QueryParameterValue] = [\"api-version\": \"3.0\"]\n\n        switch self {\n        case .languages:\n            break\n\n        case let .translate(_, sourceLanguage, targetLanguages):\n            queryParameters[\"from\"] = .string(sourceLanguage.rawValue)\n            queryParameters[\"to\"] = .array(targetLanguages.map { $0.rawValue })\n        }\n\n        return queryParameters\n    }\n\n    var mockedResponse: MockedResponse? {\n      switch self {\n      case .languages:\n        return mock(status: .ok, bodyJson: #\"{ \"languages: [\"de\", \"en\", \"fr\", \"ja\"] }\"#)\n\n      case let .translate(texts, _, _):\n        let pseudoTranslationsJson = texts.map { $0.reversed() }.joined(separator: \",\")\n        return mock(status: .ok, bodyJson: \"[\\(pseudoTranslationsJson)]\")\n      }\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Step 3: Calling your API endpoint with the Result type\n\nCall an API endpoint providing a `Decodable` type of the expected result (if any) by using one of the methods pre-implemented in the `ApiProvider` type:\n\n```Swift\n/// Performs the asynchornous request for the chosen endpoint and calls the completion closure with the result.\nperformRequest\u003cResultType: Decodable\u003e(\n    on endpoint: EndpointType,\n    decodeBodyTo: ResultType.Type,\n    completion: @escaping (Result\u003cResultType, ApiError\u003cClientErrorType\u003e\u003e) -\u003e Void\n)\n\n/// Performs the request for the chosen endpoint synchronously (waits for the result) and returns the result.\npublic func performRequestAndWait\u003cResultType: Decodable\u003e(\n    on endpoint: EndpointType,\n    decodeBodyTo bodyType: ResultType.Type\n)\n```\n\nThere's also extra methods for endpoints where you don't expect a response body:\n\n```swift\n/// Performs the asynchronous request for the chosen write-only endpoint and calls the completion closure with the result.\nperformRequest(on endpoint: EndpointType, completion: @escaping (Result\u003cEmptyBodyResponse, ApiError\u003cClientErrorType\u003e\u003e) -\u003e Void)\n\n/// Performs the request for the chosen write-only endpoint synchronously (waits for the result).\nperformRequestAndWait(on endpoint: EndpointType) -\u003e Result\u003cEmptyBodyResponse, ApiError\u003cClientErrorType\u003e\u003e\n```\n\nThe `EmptyBodyResponse` returned here is just an empty type, so you can just ignore it.\n\nHere's a full example of a call you could make with Mircoya:\n\n```Swift\nlet provider = ApiProvider\u003cMicrosoftTranslatorEndpoint\u003e()\nlet endpoint = MicrosoftTranslatorEndpoint.translate(texts: [\"Test\"], from: .english, to: [.german, .japanese, .turkish])\n\nprovider.performRequest(on: endpoint, decodeBodyTo: [String: String].self) { result in\n    switch result {\n    case let .success(translationsByLanguage):\n        // use the already decoded `[String: String]` result\n\n    case let .failure(apiError):\n        // error handling\n    }\n}\n\n// OR, if you prefer a synchronous call, use the `AndWait` variant\n\nswitch provider.performRequestAndWait(on: endpoint, decodeBodyTo: [String: String].self) {\ncase let .success(translationsByLanguage):\n    // use the already decoded `[String: String]` result\n\ncase let .failure(apiError):\n    // error handling\n}\n```\n\nNote that you can also use the throwing `get()` function of Swift 5's `Result` type instead of using a `switch`:\n\n```Swift\nprovider.performRequest(on: endpoint, decodeBodyTo: [String: String].self) { result in\n    let translationsByLanguage = try result.get()\n    // use the already decoded `[String: String]` result\n}\n\n// OR, if you prefer a synchronous call, use the `AndWait` variant\n\nlet translationsByLanguage = try provider.performRequestAndWait(on: endpoint, decodeBodyTo: [String: String].self).get()\n// use the already decoded `[String: String]` result\n```\n\nThere's even useful functional methods defined on the `Result` type like `map()`, `flatMap()` or `mapError()` and `flatMapError()`. See the \"Transforming Result\" section in [this](https://www.hackingwithswift.com/articles/161/how-to-use-result-in-swift) article for more information.\n\n### Combine Support\n\nIf you are using Combine in your project (e.g. because you're using SwiftUI), you might want to replace the calls to `performRequest(on:decodeBodyTo:)` or `performRequest(on:)` with the Combine calls `publisher(on:decodeBodyTo:)` or `publisher(on:)`. This will give you an `AnyPublisher` request stream to subscribe to. In success cases you will receive the decoded typed object, in error cases an `ApiError` object exactly like within the `performRequest` completion closure. But instead of a `Result` type you can use `sink` or `catch` from the Combine framework.\n\nFor example, the usage with Combine might look something like this:\n\n```Swift\nvar cancellables: Set\u003cAnyCancellable\u003e = []\n\nprovider.publisher(on: endpoint, decodeBodyTo: TranslationsResponse.self)\n  .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)\n  .subscribe(on: DispatchQueue.global())\n  .receive(on: DispatchQueue.main)\n  .sink(\n    receiveCompletion: { _ in }\n    receiveValue: { (translationsResponse: TranslationsResponse) in\n      // do something with the success response object\n    }\n  )\n  .catch { apiError in\n    switch apiError {\n    case let .clientError(statusCode, clientError):\n      // show an alert to customer with status code \u0026 data from clientError body\n    default:\n      logger.handleApiError(apiError)\n    }\n  }\n  .store(in: \u0026cancellables)\n```\n\n### Concurrency Support\n\nIf you are using Swift 5.5 in your project and your minimum target is iOS/tvOS 15+, macOS 12+ or watchOS 8+, you might want to use the `async` method `response` instead. For example, the usage might look something like this:\n\n```Swift\nlet result = await provider.response(on: endpoint, decodeBodyTo: TranslationsResponse.self)\n\nswitch result {\ncase let .success(translationsByLanguage):\n    // use the already decoded `[String: String]` result\n\ncase let .failure(apiError):\n    // error handling\n}\n```\n\n### Plugins\n\nThe initializer of `ApiProvider` accepts an array of `Plugin` objects. You can implement your own plugins or use one of the existing ones in the [Plugins](https://github.com/FlineDev/Microya/tree/main/Sources/Microya/Plugins) directory. Here's are the callbacks a custom `Plugin` subclass can override:\n\n```swift\n/// Called to modify a request before sending.\nmodifyRequest(_ request: inout URLRequest, endpoint: EndpointType)\n\n/// Called immediately before a request is sent.\nwillPerformRequest(_ request: URLRequest, endpoint: EndpointType)\n\n/// Called after a response has been received \u0026 decoded, but before calling the completion handler.\ndidPerformRequest\u003cResultType: Decodable\u003e(\n    urlSessionResult: (data: Data?, response: URLResponse?, error: Error?),\n    typedResult: Result\u003cResultType, ApiError\u003cEndpointType.ClientErrorType\u003e\u003e,\n    endpoint: EndpointType\n)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eToggle me to see a full custom plugin example\u003c/summary\u003e\n\nHere's a possible implementation of a `RequestResponseLoggerPlugin` that logs using `print`:\n\n```swift\nclass RequestResponseLoggerPlugin\u003cEndpointType: Endpoint\u003e: Plugin\u003cEndpointType\u003e {\n    override func willPerformRequest(_ request: URLRequest, endpoint: EndpointType) {\n        print(\"Endpoint: \\(endpoint), Request: \\(request)\")\n    }\n\n    override func didPerformRequest\u003cResultType: Decodable\u003e(\n        urlSessionResult: ApiProvider\u003cEndpointType\u003e.URLSessionResult,\n        typedResult: ApiProvider\u003cEndpointType\u003e.TypedResult\u003cResultType\u003e,\n        endpoint: EndpointType\n    ) {\n        print(\"Endpoint: \\(endpoint), URLSession result: \\(urlSessionResult), Typed result: \\(typedResult)\")\n    }\n}\n\n```\n\n\u003c/details\u003e\n\n\n\n### Shortcuts\n\n`Endpoint` provides default implementations for most of its required methods, namely:\n\n```swift\npublic var decoder: JSONDecoder { JSONDecoder() }\n\npublic var encoder: JSONEncoder { JSONEncoder() }\n\npublic var headers: [String: String] {\n    [\n        \"Content-Type\": \"application/json\",\n        \"Accept\": \"application/json\",\n        \"Accept-Language\": Locale.current.languageCode ?? \"en\"\n    ]\n}\n\npublic var queryParameters: [String: QueryParameterValue] { [:] }\n\npublic var mockedResponse: MockedResponse? { nil }\n```\n\nSo technically, the `Endpoint` type only requires you to specify the following 4 things:\n\n```swift\nprotocol Endpoint {\n    associatedtype ClientErrorType: Decodable\n    var subpath: String { get }\n    var method: HttpMethod { get }\n}\n```\n\nThis can be a time (/ code) saver for simple APIs you want to access.\nYou can also use `EmptyBodyResponse` type for `ClientErrorType` to ignore the client error body structure.\n\n### Testing\n\nMicroya supports mocking responses in your tests.\nTo do that, just initialize a different `ApiProvider` in your tests and specify with a given `delay` and `scheduler` as the `mockingBehavior` parameter.\n\nNow, instead of making actual calls, Microya will respond with the provided `mockedResponse` computed property in your `Endpoint` type.\n\nNote that the `.delay` mocking behavior is designed for use with Combine schedulers. Use `DispatchQueue.test` from the [`combine-schedulers` library](https://github.com/pointfreeco/combine-schedulers) (which is included with Microya) to control time in your tests so you don't need to actually wait for the requests when using `.delay`.\n\nFor example, you might want to add an extension in your tests to provide a `.mocked` property to use whenever you need an `ApiProvider` like so:\n\n```swift\nimport CombineSchedulers\nimport Foundation\nimport Microya\n\nlet testScheduler: AnySchedulerOf\u003cDispatchQueue\u003e = DispatchQueue.test\n\nextension ApiProvider {\n  static var mocked: ApiProvider\u003cMicrosoftTranslatorEndpoint\u003e {\n    ApiProvider\u003cMicrosoftTranslatorEndpoint\u003e(\n      baseUrl: URL(string: \"https://api.cognitive.microsofttranslator.com\")!,\n      mockingBehavior: MockingBehavior(delay: .seconds(0.5), scheduler: testScheduler.eraseToAnyScheduler()\n    )\n  }\n}\n\n```\n\nNow, in your tests you can just call `testScheduler.advance(by: .milliseconds(300))` fast-forward the time so your tests stay fast.\n\n## Donation\n\nMicroya was brought to you by [Cihat Gündüz](https://github.com/Jeehut) in his free time. If you want to thank me and support the development of this project, please **make a small donation on [PayPal](https://paypal.me/Dschee/5EUR)**. In case you also like my other [open source contributions](https://github.com/FlineDev) and [articles](https://medium.com/@Jeehut), please consider motivating me by **becoming a sponsor on [GitHub](https://github.com/sponsors/Jeehut)** or a **patron on [Patreon](https://www.patreon.com/Jeehut)**.\n\nThank you very much for any donation, it really helps out a lot! 💯\n\n\n## Contributing\n\nSee the file [CONTRIBUTING.md](https://github.com/FlineDev/Microya/blob/main/CONTRIBUTING.md).\n\n\n## License\nThis library is released under the [MIT License](http://opensource.org/licenses/MIT). See LICENSE for details.\n","funding_links":["https://paypal.me/Dschee/5EUR","https://github.com/sponsors/Jeehut","https://patreon.com/Jeehut","https://paypal.me/Dschee/5EUR)*","https://www.patreon.com/Jeehut)*"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflinedev%2Fmicroya","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflinedev%2Fmicroya","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflinedev%2Fmicroya/lists"}