{"id":20492573,"url":"https://github.com/redmadrobot/service-generator","last_synced_at":"2026-04-19T12:31:16.623Z","repository":{"id":75660759,"uuid":"130323993","full_name":"RedMadRobot/service-generator","owner":"RedMadRobot","description":null,"archived":false,"fork":false,"pushed_at":"2018-08-24T11:41:03.000Z","size":89,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-01-16T05:55:06.621Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/RedMadRobot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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}},"created_at":"2018-04-20T07:12:31.000Z","updated_at":"2018-08-24T11:32:20.000Z","dependencies_parsed_at":"2023-06-07T07:00:11.618Z","dependency_job_id":null,"html_url":"https://github.com/RedMadRobot/service-generator","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fservice-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fservice-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fservice-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fservice-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RedMadRobot","download_url":"https://codeload.github.com/RedMadRobot/service-generator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242073844,"owners_count":20067916,"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":[],"created_at":"2024-11-15T17:29:39.673Z","updated_at":"2026-04-19T12:31:11.586Z","avatar_url":"https://github.com/RedMadRobot.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ServiceGenerator\n\n\u003e Binary file can be installed via cocoapods ([link](https://github.com/RedMadRobot/cocoapods-specs)).\n\n\u003e Each time you update repository ensure to draft new release, info can be found [here](https://github.com/RedMadRobot/cocoapods-specs/blob/master/README.md).\n\nThis utility generates parsers from model objects. Only works for [HTTP Transport](https://github.com/RedMadRobot/http-transport).\n\n## Example\n\n### How to delare a service\n\nFirst of all you need to declare a protocol\n\n\n```swift\n/**\n @service\n @url http://server.com/api/entities\n @add_cookies\n @receive_cookies\n*/\nprotocol Service {\n}\n```\n\nSupported service annotations:\n\n`@service`\nAnnotate a protocol, a service will be generated upon this.\n\n`@url URL`\nBase URL for your service. Service includes this URL as initializer argument.\n\n`@add_cookies`\nService will add Cookie to requests. Cookie is passes in initializer.\n\n`@receive_cookies`\nService will save received Cookie. Cookie is passes in initializer.\n\n### How to declare methods\n\nMethods must return `ServiceCell` or `AuthorizedServiceCall`.\nModel object should be annotated with `@model` or you should write `@parser ParserName`.\n\nIf yout method return void result just use `ServiceCall\u003cVoid\u003e`.\n\n```swift\n/**\n @auto_login\n @post\n @url /paginated/{request_id}\n*/\nfunc getEntities(\n    requestIdentifier: String, // @url request_id\n    searchQuery: String,       // @query search\n    dateToken: Double,         // @json date_token\n    deviceOS: String           // @header X-Att-Deviceos\n) -\u003e ServiceCall\u003c[Entity]\u003e\n```\n\nSupported method annotations:\n`@get, @post, @put, @patch, @delete, @head, @options`.\n\n`@auto_login`\nIn case of failed request we check existent session. If it absents `Authorizer` try to renew it and repeat original request.\n`Authorizer` is passed in initializer.\n\nThe next three examples are the same.\n\n```swift\n/**\n @get\n @auto_login\n*/\nfunc method() -\u003e ServiceCall\u003cEntity\u003e\n```\n\n```swift\n/**\n @get\n*/\nfunc method() -\u003e AuthorizedServiceCall\u003cEntity\u003e\n```\n\n```swift\n/**\n @get\n @auto_login\n*/\nfunc method() -\u003e AuthorizedServiceCall\u003cEntity\u003e\n```\n\n`@url URL` relative URL for method based on service URL. This URL can include a template for URL-parameter with format `{parameter_name}`.\nParameters are passed as arguments of method. You need to annotate them with `@url parameter_name`.\n\n`@parser NAME`\nParser name to parse response body into models. Generator takes it from return model type by default.\n\nSupported method annotations:\n\n`@url NAME`\nURL parameter to insert in place of `{NAME}`.\n\n`@query NAME`\nquery parameter with `NAME=`.\n\n`@json NAME`\nПараметр требуется передать в JSON-тело запроса под именем \"NAME\": ...\n\n`@header NAME`\nПараметр требуется добавить к запросу в виде заголовка NAME.\n\n`@responseInterceptor NAME`\n`HTTPResponseInterceptor` to add to this method only.\n\n`@requestInterceptor NAME`\n`HTTPRequestInterceptor` to add this method only.\n\n`@content [json|string]` used for specify type of parser for primitive return type. In case of option `string`, response body parsed as raw string. In case of option `json` (default) response body parsed as JSON and extract first value suitable for returned type.\n\n\n### Example of EntityService\n\nLet's take a look at typical `EntityService` that obtain model type `Entity`.\n\n```swift\n/**\n This is our entity.\n \n This annotation means this is model object\n @model\n */\nclass Entity {\n    /**\n     Identifier.\n \n     This annotation for another our generator utility - Core Parser Generator\n     @json\n    */\n    let id: Int\n}\n```\nService protocol\n\n```swift\n/**\n Service for our model of type Entity.\n \n Base URL, for model Entity:\n @url https://server.com/api/entities\n\n We need to be authorized before making requests, so we use Cookie to pass session identifier for each request.\n \n Adding this annotation says we want to add Cookie to each request.\n @add_cookies\n \n Server can send us additional Cookie. This is our duty to save it.\n \n Adding this annotation says we want to save Cookie from each response.\n @receive_cookies\n */\nprotocol EntityService {\n \n    /**\n     Obtain Entity by page.\n \n \t  In case of expired session, we need to automatically renew it.\n     @auto_login\n \n     Method is GET /entities, so leave relative URL empty.\n     @get\n \n     Don't forget to write limit and offset query parameters.\n     */\n    func getEntities(\n        limit: Int,        // @query\n        offset: Int        // @query\n    ) -\u003e ServiceCall\u003c[Entity]\u003e // return array of Entity\n \n    /**\n     Obtain Entity by identifier.\n \n     Method is GET /entities/{id}\n     @get\n     @url /{id}\n \n     Pay attention how entityId is passed to {id} from URL.\n     Internal argument name is id, so this default value for annotation @url.\n     */\n    func getEntity(\n        entityId id: String // @url\n    ) -\u003e ServiceCall\u003cEntity\u003e // returns Entity\n \n}\n```\n\nThe result of generator is (comments are written manually for your understanding)\n\n```swift\nclass EntityServiceGen: EntityService {\n \n    let baseURL:    String                 // base URL, that used in EntityServiceGen.baseRequest\n    let dependency: ServiceDependency      // requests/response queues, security settings\n    var logFilter:  ServiceLogFilter       // log levels\n \n    let cookieProvider: CookieProviding    // this property generated regards to annotation @add_cookies – we take cookie from here\n    let cookieStorage: CookieStoring       // this property generated regards to annotation @receive_cookies – we store cookie here\n    let authorizer: Authorizing            // this property generated regards to annotation @auto_login — getEntities(limit:offset:)\n                                           // authorizer repeats authorization and makes error handling\n \n    // this is base interceptors for requests\n    var baseRequestInterceptors: [HTTPRequestInterceptor] {\n        return [\n            AddCookieInterceptor(cookieProvider: self.cookieProvider),       // this interceptor generated regards to annotation @add_cookies\n            LogRequestInterceptor(logLevel: self.logFilter.requestLogLevel),\n        ]\n    }\n \n    // this is base interceptors for responses\n    var baseResponseInterceptors: [HTTPResponseInterceptor] {\n        return [\n            ReceivedCookieInterceptor(cookieStorage: self.cookieStorage),    // this interceptor generated regards to annotation @receive_cookies\n            LogResponseInterceptor(logLevel: self.logFilter.responseLogLevel, isFilteringHeaders: self.logFilter.isFilteringResponseHeaders, headerFilter: self.logFilter.responseHeaderFilter),\n        ]\n    }\n \n    // this is base request for any other request. It usess baseURL\n    var baseRequest: HTTPRequest { return HTTPRequest(endpoint: self.baseURL) }\n \n    // this is transport with base interceptors, security settings and etc.\n    var transport: HTTPTransport { return HTTPTransport(session: self.dependency.session, requestInterceptors: self.baseRequestInterceptors, responseInterceptors: self.baseResponseInterceptors, useDefaultValidation: self.dependency.useDefaultValidation) }\n \n    // Initializer\n    init(\n        dependency: ServiceDependency,\n        baseURL: String = \"https://server.com/api/entities\",   // this is generate from @url annotation @url https://server.com/api/entities\n        authorizer: Authorizing,                               // this is generated from @auto_login annotation\n        cookieProvider: CookieProviding,                       // this is generated from @add_cookies annotation — this is object with Cookie for requests\n        cookieStorage: CookieStoring,                          // this is generated from @receive_cookies annotation — this is object to store Cookie from responses\n        logFilter: ServiceLogFilter = ServiceLogFilter()\n    ) {\n        self.dependency = dependency\n        self.baseURL = baseURL\n        self.logFilter = logFilter\n        self.authorizer = authorizer\n        self.cookieProvider = cookieProvider\n        self.cookieStorage = cookieStorage\n    }\n \n    // Helper method\n    func createCall\u003cPayload\u003e(main: @escaping ServiceCall\u003cPayload\u003e.Main) -\u003e ServiceCall\u003cPayload\u003e {\n        return ServiceCall(operationQueue: self.dependency.operationQueue, callbackQueue: self.dependency.completionQueue, main: main)\n    }\n \n    // Helper method\n    func createAuthorizedCall\u003cPayload\u003e(main: @escaping ServiceCall\u003cPayload\u003e.Main) -\u003e ServiceCall\u003cPayload\u003e {\n        return AuthorizedServiceCall(operationQueue: self.dependency.operationQueue, callbackQueue: self.dependency.completionQueue, authorizer: authorizer, main: main)\n    }\n \n    // override this method to check errors in response\n    func verify(response: HTTPResponse) -\u003e NSError? { return nil }\n \n    func getEntities(limit: Int, offset: Int) -\u003e AuthorizedServiceCall\u003c[Entity]\u003e {\n        return self.createAuthorizedCall() { () -\u003e ServiceCallResult\u003c[Entity]\u003e in     // as a result this will be AuthorizedServiceCall with autologin functionality\n            let request: HTTPRequest =\n                HTTPRequest(\n                    httpMethod: HTTPRequest.HTTPMethod.get,                           // @get method annotation\n                    endpoint: \"\",\n                    headers: [:],\n                    parameters: [\n                        HTTPRequestParameters(parameters: [\n                            \"limit\": limit,                                           // @query annotation for parameter\n                            \"offset\": offset,                                         // @query annotation for parameter\n                        ], encoding: HTTPRequestParameters.Encoding.url),             // your parameters serialize to query\n                    ],\n                    base: self.baseRequest\n                )\n \n            switch self.transport.send(request: request) {\n                case .success(let response):\n                    if let error = self.verify(response: response) { return ServiceCallResult.failure(error: error) }\n \n                    do {\n                        let jsonObject: Any = try response.getJSON()!\n                        let payload = EntityParser().parse(jsonObject)                // parser name generated based on Entity name\n                        return ServiceCallResult.success(payload: payload)\n                    } catch let error {\n                        return ServiceCallResult.failure(error: error as NSError)\n                    }\n \n                case .failure(let error):\n                    return ServiceCallResult.failure(error: error)\n            }\n        }\n    }\n \n    func getEntity(entityId id: String) -\u003e ServiceCall\u003cEntity\u003e {\n        return self.createCall() { () -\u003e ServiceCallResult\u003cEntity\u003e in\n            let request: HTTPRequest =\n                HTTPRequest(\n                    httpMethod: HTTPRequest.HTTPMethod.get,\n                    endpoint: \"/\\(id)\",                                               // URL generated from @url /{id} annotation and argument annotation entityId id: String // @url\n                    headers: [:], \n                    parameters: [],\n                    base: self.baseRequest\n                )\n \n            switch self.transport.send(request: request) {\n                case .success(let response):\n                    if let error = self.verify(response: response) { return ServiceCallResult.failure(error: error) }\n \n                    do {\n                        let jsonObject: Any = try response.getJSON()!\n                        let payload = EntityParser().parse(jsonObject).first!     // parser name generated based on Entity name\n                        return ServiceCallResult.success(payload: payload)\n                    } catch let error {\n                        return ServiceCallResult.failure(error: error as NSError)\n                    }\n \n                case .failure(let error):\n                    return ServiceCallResult.failure(error: error)\n            }\n        }\n    }\n \n}\n```\n\nYou can inherit `EntityServiceGen` and redefine the following properties and function\n\n```swift\nvar baseRequestInterceptors                      // set your own array of base interceptors\nvar baseResponseInterceptors                     // set your own array of base interceptors\nvar baseRequest                                  // add some configuration to reauest\nvar transport                                    // add some configuration too \nfunc verify(response: HTTPResponse) -\u003e NSError?  // check response for possible error\n```\n\n The next properties are passed in initializer, you don't need to redefine it.\n\n```swift \nlet baseURL\nlet dependency\nvar logFilter\nlet cookieProvider\nlet cookieStorage\nlet authorizer\n```\n\n## Restrictions\n\n* Generator supports only object types and arrays of them. Primitive types, generics, Dictionaries are not supported.\n\n```swift\nfunc getSome() -\u003e ServiceCall\u003cInt\u003e\n```\nwill generate an error.\n\n## Author\n\nEgor Taflanidi, et@redmadrobot.com\n\n## Support team\n\nIvan Vavilov, iv@redmadrobot.com\n\nAndrey Rozhkov, ar@redmadrobot.com","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fservice-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredmadrobot%2Fservice-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fservice-generator/lists"}