Dratini is a neat network abstraction layer.

Overview

Dratini CI Status codecov CocoaPods Carthage Swift Pacakge Manager License

Dratini is a neat network abstraction layer.
If you are looking for a solution to make your network layer neat, Dratini is your choice.
Dratini uses protocols to define network request, parameters and response, which makes your network layer more readable and testable.

Features

  • Protocol base design.
  • Auto serialization for parameters.
  • Response is observable by request id or response type.
  • UI non-blocking since request and response handling happen in background thread.
  • Request and response are interceptable by using delegate.
  • RxSwift extension is available: RxDratini

Requirements

  • Xcode 8.0+
  • Swift 3.0

Dependencies

  • Ditto: it's used for serializing Swift object into JSON compatible dictionary, mainly used for impelmenting DefaultQueryString, URLEncodedBodyData and JSONBodyData.

Usage

CocoaPods

pod 'Dratini'

Carthage

github "kevin0571/Dratini"

Swift Package Manager

dependencies: [
    .Package(url: "https://github.com/kevin0571/Dratini.git", majorVersion: 1)
]

Overview

Here are some basic steps to send out a request and observe for its response.

Setup RequestQueue:

let requestQueue = RequestQueue(baseURL: URL(string: "http://example.com"))
// Delegate and configuration are not required.
// Set the delegate(RequestQueueDelegate) if you wish to get callbacks for each step.
// RequestQueueConfiguration.default is used if configuration is not specified.

Keep a shared RequestQueue is recommended:

extension RequestQueue {
    static let shared = RequestQueue(baseURL: URL(string: "http://example.com"))
}

Describe your request, parameters and response:

struct LogInRequest: Request {
    typealias ParametersType = LogInParameters
    typealias ResponseType = LogInResponse
    
    var parameters: LogInParameters
    
    func path() -> String {
        return "/login"
    }
    
    func method() -> HTTPMethod {
        return .post
    }
}

// There are several built-in Parameters types:
// - DefaultQueryString for query string, it will mostly be used in GET request.
// - URLEncodedBodyData for URL encoded body data.
// - JSONBodyData for JSON format body data.
// - MultipartFormData for multipart form data, it will mostly be used for uploading file.
//
// In order to allow you to keep the naming convention of different platforms,
// property name of DefaultQueryString, URLEncodedBodyData and JSONBodyData will be mapped to other naming convention.
// By default property will be converted to lowercase name separated by underscore,
// e.g. accessToken will be converted to access_token. 
// You can set the mapping by overriding "serializableMapping" function.
// See more details in Ditto project's README.
struct LogInParameters: URLEncodedBodyData {
    let username: String
    let password: String
}

struct LogInResponse: Response {
    let username: String
    let name: String
    init?(data: ResponseData, response: URLResponse) {
        // - Use data.data to access raw response data.
        // - Use data.jsonObject to access JSON format dictionary.
        // - Use data.jsonArray to access JSON format array.
        // - Use data.string to access UTF8 string.
        guard let username = data.jsonObject["username"] as? String,
            let name = data.jsonObject["name"] as? String else {
            return nil
        }
        self.username = username
        self.name = name
    }
}

Send the request and observe for response:

let request = LogInRequest(parameters: LogInParameters(username: username,
                                                       password: password))
let requestID = RequestQueue.shared.add(request)
// Observe by using requestID.
// The observer will be removed by RequestQueue after the request is finished.
requestQueue.addObserver(for: requestID) { (result: Result<LogInResponse>) in
    guard let response = result.response else {
        // Show error message
        return
    }
    // Update UI by using response.username and response.name
}
// Observe a specific response type. 
// The observer is owned by an owner. The owner is held weakly by RequestQueue,
// thus the observer will be removed if owner is released.
requestQueue.addObserver(ownedBy: self) { [weak self] (result: Result<LogInResponse>) in
    // ...
}
// NOTE: observer callback is called in main thread.

Do More with Dratini

Sometimes you need to do more with Dratini, here are some features you might need, e.g. upload file, intercept different states of request and response.

Upload file:

let data = MultipartFormData()
// Append file with fileURL
data.append(fileURL: fileURL, withName: name, fileName: fileName, mimeType: "application/x-plist")  
// Append raw file data
data.append(data: fileData, withName: name, fileName: fileName, mimeType: "application/x-plist")

// Assume we've created UploadFileRequest
let request = UploadFileRequest(parameters: data)
// Send out request
// ...

Intercept states of request:

// Conform to Request with RequestDelegate to get callbacks of different states.
struct LogInRequest: Request, RequestDelegate {
    // ...
    
    func requestWillSend(_ urlRequest: inout URLRequest) {
        // Called before request is sent out.
        // You are able to modify the URLRequest: update HTTP header for example.
    }
    
    func requestDidSend(_ urlRequest: URLRequest) {
        // Called after request is sent out.
    }
    
    func request(_ urlRequest: URLRequest, didFailWith error: DRError) {
        // Called when request is failed to be sent out or response is failed to be created.
    }
}

Validate response before creating response and intercept states of response:

struct LogInResponse: Response, ResponseDelegate {
    // ...
    
    // Validate the response before it's created.
    static func validate(_ response: URLResponse) -> Bool {
        guard let httpResponse = response as? HTTPURLResponse else {
            return true
        }
        return httpResponse.statusCode >= 200 &&
            httpResponse.statusCode < 300 &&
            httpResponse.allHeaderFields["Token"] != nil
    }
    
    // Called after response is created.
    func responseDidReceive(_ response: URLResponse) {
        guard let httpResponse = response as? HTTPURLResponse,
            let token = httpResponse.allHeaderFields["Token"] else {
            return nil
        }
        // Save your token
    }
}

Having common logic for all requests and response are sometimes necessary, RequestQueueDelegate is here for you:

class MyRequestQueueDelegate: RequestQueueDelegate {
    public func requestQueue(_ requestQueue: RequestQueue, willSend request: inout URLRequest) {
        // Called before each request is sent out.
    }
    
    public func requestQueue(_ requestQueue: RequestQueue, didSend request: URLRequest) {
        // Called after each request is sent out.
    }
    
    public func requestQueue(_ requestQueue: RequestQueue, didFailWith request: URLRequest, error: DRError) {
        // Called when request is failed to be sent out or response is failed to be created.
    }
    
    public func requestQueue(_ requestQueue: RequestQueue, didReceive response: URLResponse) {
        // Called after response is created.
    }
}

extension RequestQueue {
    // Set delegate when creating RequestQueue.
    static let shared = RequestQueue(delegate: MyRequestQueueDelegate(), baseURL: URL(string: "http://example.com")!)
}

Check if request is finished and cancel it:

let isFinished = RequestQueue.shared.isFinished(requestID)
RequestQueue.shared.cancel(requestID)

Helpers

When you don't really need a Parameters or Response, you can use:

EmptyParameters
EmptyResponse

Customization

If you wish to customize query string or body data encoding, you can implement your own by adpoting QueryString or BodyData protocol.

struct MyBodyData: BodyData {
    let string: String
    
    var contentType: String {
        return "my-content-type"
    }
    
    func encode() throws -> Data {
        return string.data(using: .utf8)!
    }
}
You might also like...
Generic Network Layer created using Swift.

Generic-Network-Layer_iOS Generic Network Layer created using URLSession. Networking is an essential element in app development, and you'll need API c

Alamofire network layer

NVNetworkRequest Alamofire network layer Installation Add this to your Package dependencies: dependencies: [ .package(url: "https://github.com/vin

Async network layer with Combine
Async network layer with Combine

Version 1.0.10 Currently Available Platform Version iOS 12.0 tvOS 10.0 macOS 10.15 watchOS 3.0 macCatalyst 13.0 Hover is a Network layer which uses Ap

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

Alamofire Network Layer written in swift 5 using the protocol oriented, combine, UIKit, MVVM.

CoreAPI-iOS This project Contains Alamofire Network layer Based on Protocol Oriented Concept and Combine Framework. It is created with UIKit, Alamofir

VFNetwork is a protocol-oriented network layer that will help you assemble your requests in just a few steps.
VFNetwork is a protocol-oriented network layer that will help you assemble your requests in just a few steps.

Simple, Fast and Easy. Introduction VFNetwork is a protocol-oriented network layer that will help you assemble your requests in just a few steps. How

A network extension app to block a user input URI. Meant as a network extension filter proof of concept.
A network extension app to block a user input URI. Meant as a network extension filter proof of concept.

URIBlockNE A network extension app to block a user input URI. Meant as a network extension filter proof of concept. This is just a research effort to

NWReachability - a pure Swift library for monitoring the network connection of iOS devices using Apple's Network framework.

NWReachability is a pure Swift library for monitoring the network connection of iOS devices using Apple's Network framework.

Elegant API Abstraction for Swift

Endpoint (Deprecated) ⚠️ This project has been deprecated. Consider using Moya and MoyaSugar instead. 🚀 Elegant API Abstraction for Swift. At a Glanc

Comments
  • Feature request: optional input params

    Feature request: optional input params

    For example:

    /api/v2/location.json?
    id=Amsterdam
    &fields=all
    

    "fields" is a param but it will be always constant for me because I usually need to get everything I can with one request

    opened by gerchicov-bp 1
  • Methods to output debug information?

    Methods to output debug information?

    Current swift version has bugs with debug console output po but still support print and printDebug commands via code. Could you add log methods to input and output params?

    enhancement 
    opened by gerchicov-bp 0
Releases(1.1.2)
Owner
Kevin Lin
Facebook/Native UI Framework/ComponentKit
Kevin Lin
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
Lightweight network abstraction layer, written on top of Alamofire

TRON is a lightweight network abstraction layer, built on top of Alamofire. It can be used to dramatically simplify interacting with RESTful JSON web-

MLSDev 528 Dec 26, 2022
NSURLSession network abstraction layer, using Codable and Decodable for response and Encodable for request. ⚙️🚀

SONetworking NSURLSession network abstraction layer, using Codable and Decodable for response and Encodable for request. Project Folder and File Struc

Ahmad AlSofi 4 Jan 28, 2022
Network abstraction layer written in Swift.

Moya 15.0.0 A Chinese version of this document can be found here. You're a smart developer. You probably use Alamofire to abstract away access to URLS

Moya 14.4k Jan 4, 2023
Another network wrapper for URLSession. Built to be simple, small and easy to create tests at the network layer of your application.

Another network wrapper for URLSession. Built to be simple, small and easy to create tests at the network layer of your application. Install Carthage

Ronan Rodrigo Nunes 89 Dec 26, 2022
Say goodbye to the Fat ugly singleton Network Manager with this Network Layer

MHNetwork Protocol Oriented Network Layer Aim to avoid having bloated singleton NetworkManager Philosophy the main philosophy behind MHNetwork is to h

Mohamed Emad Hegab 19 Nov 19, 2022
Easy and lightweight network layer for creating different set of network requests like GET, POST, PUT, DELETE customizable with coders conforming to TopLevelDecoder, TopLevelEncoder

Easy and lightweight network layer for creating different set of network requests like GET, POST, PUT, DELETE customizable with coders conforming to TopLevelDecoder, TopLevelEncoder

Igor 2 Sep 16, 2022
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
DBNetworkStack is a network abstraction for fetching request and mapping them to model objects

DBNetworkStack Main Features ?? Typed network resources ?? Value oriented architecture ?? Exchangeable implementations ?? Extendable API ?? Composable

DB Systel GmbH 33 Jan 10, 2022
A generic network layer written in swift

SwiftyNet 1.0.0 A generic network layer written in swift. you can use it as an abstraction layer above Alamofire with generic returned types. Installa

Mohamed Salah Zidane 17 Oct 11, 2021