{"id":1614,"url":"https://github.com/MLSDev/TRON","last_synced_at":"2025-08-06T14:31:22.496Z","repository":{"id":3772045,"uuid":"50839675","full_name":"MLSDev/TRON","owner":"MLSDev","description":"Lightweight network abstraction layer, written on top of Alamofire","archived":false,"fork":false,"pushed_at":"2024-09-09T15:04:00.000Z","size":4203,"stargazers_count":542,"open_issues_count":0,"forks_count":49,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-11-14T13:28:05.129Z","etag":null,"topics":["abstraction","alamofire","client","ios","networking"],"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/MLSDev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"Supporting files/Framework.plist","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-01T13:02:33.000Z","updated_at":"2024-11-08T13:32:54.000Z","dependencies_parsed_at":"2022-11-28T10:33:56.781Z","dependency_job_id":"f7db7a48-6334-4096-90a9-4e0b0b207607","html_url":"https://github.com/MLSDev/TRON","commit_stats":{"total_commits":401,"total_committers":10,"mean_commits":40.1,"dds":0.05735660847880297,"last_synced_commit":"17b5861b4ad61fe550503bb679d8541cd88d3bbd"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MLSDev%2FTRON","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MLSDev%2FTRON/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MLSDev%2FTRON/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MLSDev%2FTRON/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MLSDev","download_url":"https://codeload.github.com/MLSDev/TRON/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228905547,"owners_count":17989781,"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":["abstraction","alamofire","client","ios","networking"],"created_at":"2024-01-05T20:15:51.419Z","updated_at":"2024-12-09T14:31:16.360Z","avatar_url":"https://github.com/MLSDev.png","language":"Swift","funding_links":[],"categories":["Networking","Libs","HarmonyOS","Network","Network [🔝](#readme)"],"sub_categories":["Video","Network","Other free courses","Windows Manager"],"readme":"\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/MLSDev/TRON/raw/main/TRON.png\" /\u003e\n\u003c/p\u003e\n\n![CI](https://github.com/MLSDev/TRON/workflows/CI/badge.svg)\n[![codecov.io](https://codecov.io/github/MLSDev/TRON/coverage.svg?branch=main)](https://codecov.io/github/MLSDev/TRON?branch=main)\n![CocoaPod platform](https://cocoapod-badges.herokuapp.com/p/TRON/badge.svg)\n![CocoaPod version](https://cocoapod-badges.herokuapp.com/v/TRON/badge.svg)\n[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)\n[![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg)]()\n\nTRON is a lightweight network abstraction layer, built on top of [Alamofire](https://github.com/Alamofire/Alamofire). It can be used to dramatically simplify interacting with RESTful JSON web-services.\n\n## Features\n\n- [x] Generic, protocol-based implementation\n- [x] Built-in response and error parsing\n- [x] Support for any custom mapper ([SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) implementation provided). Defaults to `Codable` protocol.\n- [x] Support for upload tasks\n- [x] Support for download tasks and resuming downloads\n- [x] Robust plugin system\n- [x] Stubbing of network requests\n- [x] Modular architecture\n- [x] Support for iOS/Mac OS X/tvOS/watchOS/Linux\n- [x] Support for CocoaPods/Swift Package Manager\n- [x] [RxSwift](https://github.com/MLSDev/TRON#rxswift) / Combine extensions\n- [x] Support for [Swift Concurrency](https://github.com/MLSDev/TRON#swift-concurrency)\n- [x] [Complete documentation](https://mlsdev.github.io/TRON/)\n\n## Overview\n\nWe designed TRON to be simple to use and also very easy to customize. After initial setup, using TRON is very straightforward:\n\n```swift\nlet request: APIRequest\u003cUser,APIError\u003e = tron.codable.request(\"me\")\nrequest.perform(withSuccess: { user in\n  print(\"Received User: \\(user)\")\n}, failure: { error in\n  print(\"User request failed, parsed error: \\(error)\")\n})\n```\n\n## Requirements\n\n- Xcode 13 and higher\n- Swift 5.3 and higher\n- iOS 11 / macOS 10.13 / tvOS 11.0 / watchOS 4.0\n\n## Installation\n\n### Swift Package Manager\n\n* Add package into Project settings -\u003e Swift Packages\n\nTRON framework includes Codable implementation. To use SwiftyJSON, `import TRONSwiftyJSON` framework. To use RxSwift wrapper, `import RxTRON`.\n\n### CocoaPods\n\n```ruby\npod 'TRON', '~\u003e 5.3.0'\n```\n\nOnly Core subspec, without SwiftyJSON dependency:\n\n```ruby\npod 'TRON/Core', '~\u003e 5.3.0'\n```\n\nRxSwift extension for TRON:\n\n```ruby\npod 'TRON/RxSwift', '~\u003e 5.3.0'\n```\n\n## Migration Guides\n\n- [TRON 5.0 Migration Guide](https://github.com/MLSDev/TRON/blob/main/Guides/5.0%20Migration%20Guide.md)\n- [TRON 4.0 Migration Guide](https://github.com/MLSDev/TRON/blob/main/Guides/4.0%20Migration%20Guide.md)\n\n## Project status\n\n`TRON` is under active development by MLSDev Inc. Pull requests are welcome!\n\n## Request building\n\n`TRON` object serves as initial configurator for `APIRequest`, setting all base values and configuring to use with baseURL.\n\n```swift\nlet tron = TRON(baseURL: \"https://api.myapp.com/\")\n```\n\nYou need to keep strong reference to `TRON` object, because it holds Alamofire.Manager, that is running all requests.\n\n### URLBuildable\n\n`URLBuildable` protocol is used to convert relative path to URL, that will be used by request.\n\n```swift\npublic protocol URLBuildable {\n    func url(forPath path: String) -\u003e URL\n}\n```\n\nBy default, `TRON` uses `URLBuilder` class, that simply appends relative path to base URL, which is sufficient in most cases. You can customize url building process globally by changing `urlBuilder` property on `TRON` or locally, for a single request by modifying `urlBuilder` property on `APIRequest`.\n\n## Sending requests\n\nTo send `APIRequest`, call `perform(withSuccess:failure:)` method on `APIRequest`:\n\n```swift\nlet alamofireRequest = request.perform(withSuccess: { result in }, failure: { error in})\n```\n\nAlternatively, you can use `performCollectingTimeline(withCompletion:)` method that contains `Alamofire.Response` inside completion closure:\n\n```swift\nrequest.performCollectingTimeline(withCompletion: { response in\n    print(response.timeline)\n    print(response.result)\n})\n```\n\nIn both cases, you can additionally chain `Alamofire.Request` methods, if you need:\n\n```swift\nrequest.perform(withSuccess: { result in }, failure: { error in }).progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in\n    print(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)\n}\n```\n\n## Response parsing\n\nGeneric `APIRequest` implementation allows us to define expected response type before request is even sent. On top of `Alamofire` `DataResponseSerializerProtocol`, we are adding one additional protocol for error-handling.\n\n```swift\npublic protocol DataResponseSerializerProtocol {\n    associatedtype SerializedObject\n\n    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -\u003e Self.SerializedObject\n}\n\npublic protocol ErrorSerializable: Error {\n    init?(serializedObject: Any?, request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?)\n}\n```\n\n### Codable\n\nParsing models using Swift4 `Codable` protocol is simple, implement `Codable` protocol:\n\n```swift\nstruct User: Codable {\n  let name : String\n  let id: Int\n}\n```\n\nAnd send a request:\n\n```swift\nlet request: APIRequest\u003cUser,APIError\u003e = tron.codable.request(\"me\")\nrequest.perform(withSuccess: { user in\n  print(\"Received user: \\(user.name) with id: \\(user.id)\")\n})\n```\n\nIt's possible to customize decoders for both model and error parsing:\n\n```swift\nlet userDecoder = JSONDecoder()\n\nlet request : APIRequest\u003cUser,APIError\u003e = tron.codable(modelDecoder: userDecoder).request(\"me\")\n```\n\n### JSONDecodable\n\n`TRON` provides `JSONDecodable` protocol, that allows us to parse models using [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON):\n\n```swift\npublic protocol JSONDecodable {\n    init(json: JSON) throws\n}\n```\n\nTo parse your response from the server using `SwiftyJSON`, all you need to do is to create `JSONDecodable` conforming type, for example:\n\n```swift\nclass User: JSONDecodable {\n  let name : String\n  let id: Int\n\n  required init(json: JSON) {\n    name = json[\"name\"].stringValue\n    id = json[\"id\"].intValue\n  }\n}\n```\n\nAnd send a request:\n\n```swift\nlet request: APIRequest\u003cUser,MyAppError\u003e = tron.swiftyJSON.request(\"me\")\nrequest.perform(withSuccess: { user in\n  print(\"Received user: \\(user.name) with id: \\(user.id)\")\n})\n```\n\nThere are also default implementations of `JSONDecodable` protocol for Swift built-in types like String, Int, Float, Double and Bool, so you can easily do something like this:\n\n```swift\nlet request : APIRequest\u003cString,APIError\u003e = tron.swiftyJSON.request(\"status\")\nrequest.perform(withSuccess: { status in\n    print(\"Server status: \\(status)\") //\n})\n```\n\nYou can also use `Alamofire.Empty` struct in cases where you don't care about actual response.\n\nSome concepts for response serialization, including array response serializer, are described in [Container Types Parsing document](https://github.com/MLSDev/TRON/blob/main/Docs/ContainerTypesParsing.md)\n\nIt's possible to customize `JSONSerialization.ReadingOptions`, that are used by `SwiftyJSON.JSON` object while parsing data of the response:\n\n```swift\nlet request : APIRequest\u003cString, APIError\u003e = tron.swiftyJSON(readingOptions: .allowFragments).request(\"status\")\n```\n\n## Swift Concurrency\n\nSending requests using Swift Concurrency is done via a proxy object `RequestSender`(or `DownloadRequestSender` for download requests). Simple usage example:\n\n```swift\nlet request : APIRequest\u003cUser, APIError\u003e = tron.codable.request(\"/me\")\ndo {\n let user = try await request.sender().value\n  // user variable contains User type\n} catch {\n  // Network request failed\n}\n```\n\nIf you prefer to receive result, containing either successful Model, or ErrorModel, you can do that too:\n\n```swift\nlet request : APIRequest\u003cUser, APIError\u003e = tron.codable.request(\"/me\")\nlet result = await request.sender().result\n// result is Result\u003cUser,APIError\u003e\n```\n\nThere is also `response` async property, containing all request information, if you need it:\n\n```swift\nlet request : APIRequest\u003cUser, APIError\u003e = tron.codable.request(\"/me\")\nlet response = await request.sender().response\n// response: AFDataResponse\u003cModel\u003e\n```\n\n### Upload request\n\nFor upload requests, it's useful to monitor upload progress, and show it to the user:\n\n```swift\nlet request : APIRequest\u003cUser, APIError\u003e = tron.codable.request(\"/me/profile_picture\")\n  .upload(\"/post\", fromFileAt: urlForResource(\"cat\", withExtension: \"jpg\"))\n  .method(.post)  \n\nlet sender = request.sender()\nTask {\n    for await progress in sender.uploadProgress {\n      // Update progress view, progress: Progress\n    }\n}\nlet result = await sender.result\n```\n\n### Download request\n\nSimilarly to upload requests, download requests have downloadProgress property implemented as async sequence:\n\n```swift\nTask {\n    for await progress in sender.downloadProgress {\n      // Update download view, progress: Progress\n    }\n}\n```\n\nIf you only care about downloaded file URL, and not parsed data model, you can await responseURL property on request sender:\n\n```swift\nlet destination = Alamofire.DownloadRequest.suggestedDownloadDestination()\nlet request: DownloadAPIRequest\u003cURL, APIError\u003e = tron\n            .download(\"/download\",\n                      to: destination,\n                      responseSerializer: FileURLPassthroughResponseSerializer())\ndo {\n  let fileURL = try await request.sender().responseURL \n} catch {\n  // Handle error\n}\n```\n\n## RxSwift\n\n```swift\nlet request : APIRequest\u003cFoo, APIError\u003e = tron.codable.request(\"foo\")\n_ = request.rxResult().subscribe(onNext: { result in\n    print(result)\n})\n```\n\n```swift\nlet multipartRequest : UploadAPIRequest\u003cFoo,APIError\u003e = tron.codable.uploadMultipart(\"foo\", formData: { _ in })\nmultipartRequest.rxResult().subscribe(onNext: { result in\n    print(result)\n})\n```\n\n### Error handling\n\n`TRON` includes built-in parsing for errors. `APIError` is an implementation of `ErrorSerializable` protocol, that includes several useful properties, that can be fetched from unsuccessful request:\n\n```swift\nrequest.perform(withSuccess: { response in }, failure: { error in\n    print(error.request) // Original URLRequest\n    print(error.response) // HTTPURLResponse\n    print(error.data) // Data of response\n    print(error.fileURL) // Downloaded file url, if this was a download request\n    print(error.error) // Error from Foundation Loading system\n    print(error.serializedObject) // Object that was serialized from network response\n  })\n```\n\n## CRUD\n\n```swift\nstruct Users\n{\n    static let tron = TRON(baseURL: \"https://api.myapp.com\")\n\n    static func create() -\u003e APIRequest\u003cUser,APIError\u003e {\n      tron.codable.request(\"users\").post()\n    }\n\n    static func read(id: Int) -\u003e APIRequest\u003cUser, APIError\u003e {\n        tron.codable.request(\"users/\\(id)\")\n    }\n\n    static func update(id: Int, parameters: [String:Any]) -\u003e APIRequest\u003cUser, APIError\u003e {\n      tron.codable.request(\"users/\\(id)\").put().parameters(parameters)\n    }\n\n    static func delete(id: Int) -\u003e APIRequest\u003cUser,APIError\u003e {\n      tron.codable.request(\"users/\\(id)\").delete()\n    }\n}\n```\n\nUsing these requests is really simple:\n\n```swift\nUsers.read(56).perform(withSuccess: { user in\n  print(\"received user id 56 with name: \\(user.name)\")\n})\n```\n\nIt can be also nice to introduce namespacing to your API:\n\n```swift\nenum API {}\nextension API {\n  enum Users {\n    // ...\n  }\n}\n```\n\nThis way you can call your API methods like so:\n\n```swift\nAPI.Users.delete(56).perform(withSuccess: { user in\n  print(\"user \\(user) deleted\")\n})\n```\n\n## Stubbing\n\nStubbing is built right into `APIRequest` itself. All you need to stub a successful request is to set apiStub property and turn stubbingEnabled on:\n\n```swift\nAPI.Users.get(56)\n         .stub(with: APIStub(data: User.fixture().asData))\n         .perform(withSuccess: { stubbedUser in\n           print(\"received stubbed User model: \\(stubbedUser)\")\n})\n```\n\nStubbing can be enabled globally on `TRON` object or locally for a single `APIRequest`. Stubbing unsuccessful requests is easy as well:\n\n```swift\nAPI.Users.get(56)\n         .stub(with: APIStub(error: CustomError()))\n         .perform(withSuccess: { _ in },\n                  failure: { error in\n  print(\"received stubbed api error\")\n})\n```\n\nYou can also optionally delay stubbing time:\n\n```swift\nrequest.apiStub.stubDelay = 1.5\n```\n\n## Upload\n\n* From file:\n\n```swift\nlet request = tron.codable.upload(\"photo\", fromFileAt: fileUrl)\n```\n\n* Data:\n\n```swift\nlet request = tron.codable.upload(\"photo\", data: data)\n```\n\n* Stream:\n\n```swift\nlet request = tron.codable.upload(\"photo\", fromStream: stream)\n```\n\n* Multipart-form data:\n\n```swift\nlet request: UploadAPIRequest\u003cEmptyResponse,MyAppError\u003e = tron.codable.uploadMultipart(\"form\") { formData in\n    formData.append(data, withName: \"cat\", mimeType: \"image/jpeg\")\n}\nrequest.perform(withSuccess: { result in\n    print(\"form sent successfully\")\n})\n```\n\n## Download\n\n```swift\nlet responseSerializer = TRONDownloadResponseSerializer { _,_, url,_ in url }\nlet request: DownloadAPIRequest\u003cURL?, APIError\u003e = tron.download(\"file\",\n                                                                to: destination,\n                                                                responseSerializer: responseSerializer)\n```\n\n## Plugins\n\n`TRON` includes plugin system, that allows reacting to most of request events.\n\nPlugins can be used globally, on `TRON` instance itself, or locally, on concrete `APIRequest`. Keep in mind, that plugins that are added to `TRON` instance, will be called for each request. There are some really cool use-cases for global and local plugins.\n\nBy default, no plugins are used, however two plugins are implemented as a part of `TRON` framework.\n\n### NetworkActivityPlugin\n\n`NetworkActivityPlugin` serves to monitor requests and control network activity indicator in iPhone status bar. This plugin assumes you have only one `TRON` instance in your application.\n\n```swift\nlet tron = TRON(baseURL: \"https://api.myapp.com\", plugins: [NetworkActivityPlugin()])\n```\n\n### NetworkLoggerPlugin\n\n`NetworkLoggerPlugin` is used to log responses to console in readable format. By default, it prints only failed requests, skipping requests that were successful.\n\n### Local plugins\n\nThere are some very cool concepts for local plugins, some of them are described in dedicated [PluginConcepts](Docs/PluginConcepts.md) page.\n\n## Alternatives\n\nWe are dedicated to building best possible tool for interacting with RESTful web-services. However, we understand, that every tool has it's purpose, and therefore it's always useful to know, what other tools can be used to achieve the same goal.\n\n`TRON` was heavily inspired by [Moya framework](https://github.com/Moya/Moya) and LevelUPSDK, which is no longer available in open-source.\n\n## License\n\n`TRON` is released under the MIT license. See LICENSE for details.\n\n## About MLSDev\n\n[\u003cimg src=\"https://github.com/MLSDev/development-standards/raw/master/mlsdev-logo.png\" alt=\"MLSDev.com\"\u003e][mlsdev]\n\n`TRON` is maintained by [MLSDev, Inc.][mlsdev] We specialize in providing all-in-one solution in mobile and web development. Our team follows Lean principles and works according to agile methodologies to deliver the best results reducing the budget for development and its timeline.\n\nFind out more [here][mlsdev] and don't hesitate to [contact us][contact]!\n\n[mlsdev]: https://mlsdev.com\n[contact]: https://mlsdev.com/contact-us\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMLSDev%2FTRON","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMLSDev%2FTRON","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMLSDev%2FTRON/lists"}