An elegant yet powerful iOS networking layer inspired by ActiveRecord.

Overview

RxAlamoRecord

Version Platform Language: Swift License

Written in Swift 5

AlamoRecord 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 and the concepts behind the ActiveRecord pattern to create a networking layer that makes interacting with your API easier than ever.

Requirements

  • iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
  • Xcode 10.2+
  • Swift 5.1

Installation

AlamoRecord is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'AlamoRecord'

Getting Started

The power of AlamoRecord lies within four main components.

AlamoRecordObject

These 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.

RequestManager

These 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.

AlamoRecordURL

These store all of the information needed in order to make a proper request through instances of RequestManager's.

AlamoRecordError

These 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.

Example

Let's assume we are developing an application that allows user's to interact with each other via posts.

Step 1

We first need to create a class that conforms to AlamoRecordURL. Let's name it ApplicationURL:

ApplicationURL

class ApplicationURL: AlamoRecordURL {

    var absolute: String {
        return "https://jsonplaceholder.typicode.com/\(url)"
    }
    
    private var url: String
    
    required init(url: String) {
        self.url = url
    }
}

Notice 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.

Step 2

This 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.

Let'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:

{
	"status_code": 401,
	"message": "You are not authorized to make this request.",
}

Our class structure would then look very similar to this:

ApplicationError

class ApplicationError: AlamoRecordError {

    let statusCode: Int?
    let message: String?
    
    private enum CodingKeys: String, CodingKey {
        case statusCode = "status_code"
        case message
    }
    
}

Step 3

We 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 .

ApplicationRequestManager

// IDType should be of type String or Int
class ApplicationRequestManager: RequestManager<ApplicationURL, ApplicationError, IDType> {
   	
    static var `default`: ApplicationRequestManager = ApplicationRequestManager()
    
    init() {
    	// See the Configuration documentation for all possible options
        super.init(configuration: Configuration())
    }
}

Step 4

The 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.

Post

// IDType should be of type String or Int
class Post: AlamoRecordObject<ApplicationURL, ApplicationError, IDType> {

    let userId: Int
    let title: String
    let body: String
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        body = try container.decode(String.self, forKey: .body)
        try super.init(from: decoder)
    }
    
    override class var root: String {
        return "post"
    }
    
    override class var requestManager: RequestManager<ApplicationURL, ApplicationError, IDType> {
        return ApplicationRequestManager
    }
    
    private enum CodingKeys: String, CodingKey {
        case userId
        case title
        case body
    }

}

With this class definition, we would expect each Post json to look like this:

{
	"userId": 1,
	"id": 1,
	"title": "This is a post's title",
	"body": "This is the post's body"
}

If our Post object was encapsulated in an object like this:

{
	"post": {
		"userId": 1,
		"id": 1,
		"title": "This is a post's title",
		"body": "This is the post's body"
	}
}

we would only simply need to override the keyPath in the class declaration:

override class var keyPath: String? {
     return "post"
}

Notice 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.

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.

Getting all instances of Post

GET https://jsonplaceholder.typicode.com/posts

Post.all(success: { (posts: [Post]) in
   // Do something with the posts
}) { (error) in
   // Handle the error      
}

Creating an instance of Post

POST https://jsonplaceholder.typicode.com/posts

let parameters: [String: Any] = ["userId": user.id,
                                 "title": title,
                                 "body": body]
                                    
Post.create(parameters: parameters, success: { (post: Post) in
	// Do something with the post          
}) { (error) in
	// Handle the error            
}

Finding an instance of Post

GET https://jsonplaceholder.typicode.com/posts/1

Post.find(id: 1, success: { (post: Post) in
	// Do something with the post
}) { (error) in
   	// Handle the error        
}

Updating an instance of Post

PUT https://jsonplaceholder.typicode.com/posts/1

let parameters: [String: Any] = ["userId": user.id,
                                 "title": title,
                                 "body": body]
                                    
post.update(parameters: parameters, success: { (post: Post) in
	// Do something with the post     
}) { (error) in
	// Handle the error     
}

This can also be done at the class level:

Post.update(id: 1, parameters: parameters, success: { (post: Post) in
	// Do something with the post    
}) { (error) in
   	// Handle the error        
}

Destroying an instance of Post

DELETE https://jsonplaceholder.typicode.com/posts/1

post.destroy(id: 1, success: { 
	// The post is now destroyed       
}) { (error) in
	// Handle the error   
}

This can also be done at the class level:

Post.destroy(id: 1, success: { 
	// The post is now destroyed       
}) { (error) in
	// Handle the error   
}

Uploading a file

requestManager.upload(url: url,
                      multipartFormData: data,
                      multipartFormDataName: dataName,
                      success: { (any: Any?) in
   	// Upload was successful                                           
}) { (error) in
	// Handle the error      
}

Downloading a file

requestManager.download(url: url,
                        destination: destination,
                        progress: { (progress) in
    // Check the progress                                        
}, success: { (url) in
    // Do something with the url            
}) { (error) in
    // Handle the error        
}

Providing 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.

Download the example project to see just how easy creating an application that interacts with an API is when using AlamoRecord!

Author

Original concept designed by Rick Pernikoff. AlamoRecord implementation by Dalton Hinterscher.

License

AlamoRecord is available under the MIT license. See the LICENSE file for more info.

You might also like...
Approov-service-ios-swift-grpc - Approov service layer for iOS clients using GRPC

Approov Service for GRPC A wrapper for the Approov SDK to enable easy integratio

 Layer + Parse iOS Example (Swift)
Layer + Parse iOS Example (Swift)

Note: I no longer actively working on this project. If you encounter any problem, please open an issue and hopefully the community will help out. If y

iOS 15, MVVM, Async Await, Core Data, Abstract Network Layer, Repository & DAO design patterns, SwiftUI and Combine
iOS 15, MVVM, Async Await, Core Data, Abstract Network Layer, Repository & DAO design patterns, SwiftUI and Combine

iOS 15, MVVM, Async Await, Core Data, Abstract Network Layer, Repository & DAO design patterns, SwiftUI and Combine

🌏 A zero-dependency networking solution for building modern and secure iOS, watchOS, macOS and tvOS applications.
🌏 A zero-dependency networking solution for building modern and secure iOS, watchOS, macOS and tvOS applications.

A zero-dependency networking solution for building modern and secure iOS, watchOS, macOS and tvOS applications. πŸš€ TermiNetwork was tested in a produc

Asynchronous socket networking library for Mac and iOS

CocoaAsyncSocket CocoaAsyncSocket provides easy-to-use and powerful asynchronous socket libraries for macOS, iOS, and tvOS. The classes are described

A delightful networking framework for iOS, macOS, watchOS, and tvOS.
A delightful networking framework for iOS, macOS, watchOS, and tvOS.

AFNetworking is a delightful networking library for iOS, macOS, watchOS, and tvOS. It's built on top of the Foundation URL Loading System, extending t

foursquare iOS networking library

FSNetworking foursquare's iOS networking library FSN is a small library for HTTP networking on iOS. It comprises a single class, FSNConnection, and se

Extensible HTTP Networking for iOS

Bridge Simple Typed JSON HTTP Networking in Swift 4.0 GET GETDict("http://httpbin.org/ip").execute(success: { (response) in let ip: Dict = respo

Lightweight Networking and Parsing framework made for iOS, Mac, WatchOS and tvOS.
Lightweight Networking and Parsing framework made for iOS, Mac, WatchOS and tvOS.

NetworkKit A lightweight iOS, Mac and Watch OS framework that makes networking and parsing super simple. Uses the open-sourced JSONHelper with functio

Comments
  • Swift 4.2 support

    Swift 4.2 support

    Hi,

    Any plans to update this library to support Swift 4.2? This is needed on Xcode 10/iOS 12. AlamofireObjectMapper has been updated to support 4.2 version: https://github.com/tristanhimmelman/AlamofireObjectMapper/releases

    opened by MindaugasJucius 2
Releases(2.0.0)
Owner
Tunespeak
Tunespeak
πŸ“‘ RealHTTP is a lightweight yet powerful client-side HTTP library.

RealHTTP RealHTTP is a lightweight yet powerful client-side HTTP library. Our goal is make an easy to use and effortless http client for Swift. Featur

Immobiliare Labs 233 Jan 7, 2023
Type-safe networking abstraction layer that associates request type with response type.

APIKit APIKit is a type-safe networking abstraction layer that associates request type with response type. // SearchRepositoriesRequest conforms to Re

Yosuke Ishikawa 1.9k Dec 30, 2022
Sherlock Holmes of the networking layer. :male_detective:

ResponseDetective is a non-intrusive framework for intercepting any outgoing requests and incoming responses between your app and your server for debu

Netguru 1.9k Dec 24, 2022
Advanced Networking Layer Using Alamofire with Unit Testing

Advanced Networking Layer Using Alamofire with Unit Testing

Ali Fayed 8 May 23, 2022
GXBaseAPI - GARPIX Networking Layer

GXBaseAPI GARPIX Networking Layer URLSession + Combine + Codable + Generics ВсС

GARPIX iOS team 2 Jan 21, 2022
πŸ€΅πŸ½β€β™€οΈ Janet β€” A thin HTTP networking layer built on URLSession for simple, declarative endpoint specification leveraging the power of async/await.

????‍♀️ Janet β€” Just another networking kit β€” A thin HTTP networking layer built on URLSession for simple, declarative endpoint specification leveragi

Niklas Holloh 3 Sep 6, 2022
Elegant network abstraction layer in Swift.

Elegant network abstraction layer in Swift. δΈ­ζ–‡ Design Features Requirements Communication Installation Usage Base Usage - Target - Request - Download

null 100 Dec 9, 2022
Elegant HTTP Networking in Swift

Alamofire is an HTTP networking library written in Swift. Features Component Libraries Requirements Migration Guides Communication Installation Usage

Alamofire 38.7k Jan 8, 2023
Swift Express is a simple, yet unopinionated web application server written in Swift

Documentation <h5 align="right"><a href="http://demo.swiftexpress.io/">Live ?? server running Demo <img src="https://cdn0.iconfinder.com/data/icons/

Crossroad Labs 850 Dec 2, 2022
Rayon - Yet another SSH machine manager for macOS

Rayon A server monitor tool for linux based machines using remote proc file syst

Lakr Aream 2.3k Jan 8, 2023