{"id":1665,"url":"https://github.com/tunespeak/AlamoRecord","last_synced_at":"2025-08-02T04:32:16.641Z","repository":{"id":56587475,"uuid":"89876931","full_name":"tunespeak/AlamoRecord","owner":"tunespeak","description":"An elegant yet powerful iOS networking layer inspired by ActiveRecord.","archived":false,"fork":false,"pushed_at":"2020-10-30T01:18:43.000Z","size":839,"stargazers_count":18,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-08-15T19:06:49.505Z","etag":null,"topics":["activerecord","alamofire","ios","json","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/tunespeak.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}},"created_at":"2017-04-30T21:00:00.000Z","updated_at":"2023-02-05T15:26:11.000Z","dependencies_parsed_at":"2022-08-15T21:30:59.848Z","dependency_job_id":null,"html_url":"https://github.com/tunespeak/AlamoRecord","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tunespeak%2FAlamoRecord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tunespeak%2FAlamoRecord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tunespeak%2FAlamoRecord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tunespeak%2FAlamoRecord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tunespeak","download_url":"https://codeload.github.com/tunespeak/AlamoRecord/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228439075,"owners_count":17920017,"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":["activerecord","alamofire","ios","json","networking"],"created_at":"2024-01-05T20:15:52.764Z","updated_at":"2024-12-06T08:31:25.491Z","avatar_url":"https://github.com/tunespeak.png","language":"Swift","funding_links":[],"categories":["Networking"],"sub_categories":["Video"],"readme":"![RxAlamoRecord](https://raw.githubusercontent.com/tunespeak/AlamoRecord/d89104bf456bfded4f0406b77488ab412ed223ce/AlamoRecord/Assets/alamorecord_banner.png)\n\n[![Version](https://img.shields.io/cocoapods/v/AlamoRecord.svg?style=flat)](http://cocoapods.org/pods/AlamoRecord)\n[![Platform](https://img.shields.io/cocoapods/p/AlamoRecord.svg?style=flat)](http://cocoapods.org/pods/AlamoRecord)\n\u003ca href=\"https://developer.apple.com/swift\"\u003e\u003cimg src=\"https://img.shields.io/badge/swift-5.1-4BC51D.svg?style=flat\" alt=\"Language: Swift\" /\u003e\u003c/a\u003e\n[![License](https://img.shields.io/cocoapods/l/AlamoRecord.svg?style=flat)](http://cocoapods.org/pods/AlamoRecord)\n\n## Written in Swift 5\n\nAlamoRecord is a powerful yet simple framework that eliminates the often complex networking layer that exists between your networking framework and your application. AlamoRecord uses the power of [Alamofire](https://github.com/Alamofire/Alamofire) and the concepts behind the [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) pattern to create a networking layer that makes interacting with your API easier than ever.\n\n## Requirements\n\n- iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+\n- Xcode 10.2+\n- Swift 5.1\n\n## Installation\n\nAlamoRecord is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'AlamoRecord'\n```\n\n## Getting Started\n\nThe power of AlamoRecord lies within four main components.\n\n### `AlamoRecordObject`\n\nThese are data objects that are retrieved from an API. All data objects that inherit from this class automatically receive built in helpers such as create, find, update, and destroy. This is also known as [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete).\n\n### `RequestManager`\nThese are the work horses of AlamoRecord. They contain all of the networking helpers that AlamoRecord needs in order to create your application's networking layer. They are responsible for each request that your `AlamoRecordObject`'s make. They also contain upload and download helpers.\n\n### `AlamoRecordURL`\nThese store all of the information needed in order to make a proper request through instances of `RequestManager`'s.\n\n### `AlamoRecordError`\nThese are errors that are returned from an API. On a failed request, the JSON returned will be mapped from the predefined fields that are setup.\n\n\n## Example\nLet's assume we are developing an application that allows user's to interact with each other via posts.\n\n### Step 1\nWe first need to create a class that conforms to `AlamoRecordURL`. Let's name it `ApplicationURL`:\n\n#### `ApplicationURL`\n\n```swift\nclass ApplicationURL: AlamoRecordURL {\n\n    var absolute: String {\n        return \"https://jsonplaceholder.typicode.com/\\(url)\"\n    }\n    \n    private var url: String\n    \n    required init(url: String) {\n        self.url = url\n    }\n}\n\n```\nNotice how you only need to pass the path and not the domain to each instance that conforms to `AlamoRecordURL`. That's because you set the domain of each instance in the `absolute` variable. \n\n### Step 2\n\nThis step can be ignored if your server does not return back custom error messages. If this is the case, then base `AlamoRecordError` objects can be used in return. \n\nLet's assume our API returns custom error messages. Let's create a class that inherits from `AlamoRecordError` and name it `ApplicationError`. Let's also assume our JSON structure looks similar to this on failed requests:\n\n```json\n{\n\t\"status_code\": 401,\n\t\"message\": \"You are not authorized to make this request.\",\n}\n```\n\nOur class structure would then look very similar to this:\n\n#### `ApplicationError`\n\n```swift\nclass ApplicationError: AlamoRecordError {\n\n    let statusCode: Int?\n    let message: String?\n    \n    private enum CodingKeys: String, CodingKey {\n        case statusCode = \"status_code\"\n        case message\n    }\n    \n}\n```\n\n### Step 3\n\nWe next need to create an instance of a `RequestManager` and pass in the `ApplicationURL` and `ApplicationError` we just created to the inheritance structure to satisfy the generic requirements. Let's name it `ApplicationRequestManager `.\n\n#### `ApplicationRequestManager`\n\n```swift\n// IDType should be of type String or Int\nclass ApplicationRequestManager: RequestManager\u003cApplicationURL, ApplicationError, IDType\u003e {\n   \t\n    static var `default`: ApplicationRequestManager = ApplicationRequestManager()\n    \n    init() {\n    \t// See the Configuration documentation for all possible options\n        super.init(configuration: Configuration())\n    }\n}\n```\n\n### Step 4\n\nThe final step is to create data objects inheriting from `AlamoRecordObject` that our application needs from our API. In this example, we only have one object named `Post`. \n\n#### `Post`\n\n```swift\n// IDType should be of type String or Int\nclass Post: AlamoRecordObject\u003cApplicationURL, ApplicationError, IDType\u003e {\n\n    let userId: Int\n    let title: String\n    let body: String\n    \n    required init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        title = try container.decode(String.self, forKey: .title)\n        body = try container.decode(String.self, forKey: .body)\n        try super.init(from: decoder)\n    }\n    \n    override class var root: String {\n        return \"post\"\n    }\n    \n    override class var requestManager: RequestManager\u003cApplicationURL, ApplicationError, IDType\u003e {\n        return ApplicationRequestManager\n    }\n    \n    private enum CodingKeys: String, CodingKey {\n        case userId\n        case title\n        case body\n    }\n\n}\n```\n\nWith this class definition, we would expect each `Post` json to look like this:\n\n```json\n{\n\t\"userId\": 1,\n\t\"id\": 1,\n\t\"title\": \"This is a post's title\",\n\t\"body\": \"This is the post's body\"\n}\n```\n\nIf our `Post` object was encapsulated in an object like this:\n\n```json\n{\n\t\"post\": {\n\t\t\"userId\": 1,\n\t\t\"id\": 1,\n\t\t\"title\": \"This is a post's title\",\n\t\t\"body\": \"This is the post's body\"\n\t}\n}\n```\n\nwe would only simply need to override the `keyPath` in the class declaration:\n\n```swift\noverride class var keyPath: String? {\n     return \"post\"\n}\n```\n\nNotice how the `requestManager` class variable is overrided and returns the `ApplicationRequestManager` just created in step 3. Also notice how the `root` class variable is overrided. Both of these overrides are required for each instance that **directly** inherits from `AlamoRecordObject`. \n\n##### That's it! With just a few lines of code, your networking layer to your application is ready to be used. In the next section, we see all the built in helpers we get by using AlamoRecord.\n\n### Getting all instances of `Post` \n\n`GET` [https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)\n\n```swift\nPost.all(success: { (posts: [Post]) in\n   // Do something with the posts\n}) { (error) in\n   // Handle the error      \n}\n```\n\n### Creating an instance of `Post`\n\n`POST` [https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)\n\n```swift\nlet parameters: [String: Any] = [\"userId\": user.id,\n                                 \"title\": title,\n                                 \"body\": body]\n                                    \nPost.create(parameters: parameters, success: { (post: Post) in\n\t// Do something with the post          \n}) { (error) in\n\t// Handle the error            \n}\n```\n\n### Finding an instance of `Post`\n\n`GET` [https://jsonplaceholder.typicode.com/posts/1](https://jsonplaceholder.typicode.com/posts/1)\n\n```swift\nPost.find(id: 1, success: { (post: Post) in\n\t// Do something with the post\n}) { (error) in\n   \t// Handle the error        \n}\n```\n\n### Updating an instance of `Post`\n\n`PUT` [https://jsonplaceholder.typicode.com/posts/1](https://jsonplaceholder.typicode.com/posts/1)\n\n```swift\nlet parameters: [String: Any] = [\"userId\": user.id,\n                                 \"title\": title,\n                                 \"body\": body]\n                                    \npost.update(parameters: parameters, success: { (post: Post) in\n\t// Do something with the post     \n}) { (error) in\n\t// Handle the error     \n}\n```\nThis can also be done at the class level:\n\n```swift\nPost.update(id: 1, parameters: parameters, success: { (post: Post) in\n\t// Do something with the post    \n}) { (error) in\n   \t// Handle the error        \n}\n```\n\n### Destroying an instance of `Post`\n\n`DELETE` [https://jsonplaceholder.typicode.com/posts/1](https://jsonplaceholder.typicode.com/posts/1)\n\n```swift\npost.destroy(id: 1, success: { \n\t// The post is now destroyed       \n}) { (error) in\n\t// Handle the error   \n}\n```\nThis can also be done at the class level:\n\n```swift\nPost.destroy(id: 1, success: { \n\t// The post is now destroyed       \n}) { (error) in\n\t// Handle the error   \n}\n```\n\n### Uploading a file\n\n```swift\nrequestManager.upload(url: url,\n                      multipartFormData: data,\n                      multipartFormDataName: dataName,\n                      success: { (any: Any?) in\n   \t// Upload was successful                                           \n}) { (error) in\n\t// Handle the error      \n}\n```\n\n### Downloading a file\n\n```swift\nrequestManager.download(url: url,\n                        destination: destination,\n                        progress: { (progress) in\n    // Check the progress                                        \n}, success: { (url) in\n    // Do something with the url            \n}) { (error) in\n    // Handle the error        \n}\n```\nProviding a destination is optional. If a destination is not provided, then the file will be saved to a temporary location. This file will be overwritten if another download request is made without providing a destination.\n\n#### Download the example project to see just how easy creating an application that interacts with an API is when using AlamoRecord!\n\n## Author\n\nOriginal concept designed by [Rick Pernikoff](https://github.com/rickpern). AlamoRecord implementation by [Dalton Hinterscher](https://github.com/daltron).\n\n## License\n\nAlamoRecord is available under the MIT license. See the LICENSE file for more info.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftunespeak%2FAlamoRecord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftunespeak%2FAlamoRecord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftunespeak%2FAlamoRecord/lists"}