/htp

A tiny and simple HTTP client for RESTful APIs, written in Swift.

Primary LanguageSwift

Htp

A tiny and simple HTTP client for RESTful APIs, written in Swift.

Features

  • Build on top of Swift Codable (No more JSON encoding/decoding headhack).
  • Really clean syntax / few lines of code thanks to Swift 5 Result type.
  • Customizable with default headers and default HTTP session.
  • cURL like logs in the console for debugging purpose.
  • HTTP Headers and Media types libraries.
  • Crossplatform (Linux, macOS, iOS, tvOS, watchOS)
  • No dependency !

Setup

Add the Swift package https://github.com/vpusher/htp as a new dependency:

  • From another package, edit your Package.swift file,
dependencies: [
    ...
    .package(
        url: "https://github.com/vpusher/htp",
        from: "0.9.3"
    )
]
  • From an XCode project, select File > Swift Packages > Add Package Dependency and enter its repository URL.

That's it.

Define a model/entity

struct User: Encodable, Decodable {
   let id: Int? = nil
   let name: String
   let age: Int
}

Define an endpoint

import Htp

struct API {

    struct Users {
        static func all() -> Endpoint<Void, [User]> {
            return Endpoint(
                method: .get,
                path: "/users"
            )
        }
        
        static func one(id: Int) -> Endpoint<Void, User> {
            return Endpoint(
                method: .get,
                path: "/users/\(id)"
            )
        }
        
        static func create(name: String, age: Int) -> Endpoint<User, User> {
            return Endpoint(
                method: .post,
                path: "/users",
                body: User(name: name, age: age)
            )
        }
    }

}

Use

import Htp

Client.shared.fetch(API.Users.all()) { (result) in
    switch result {
    case .success(let users, let response):
        print(users)
        break
    case .failure(let error):
        print(error)
    }
}

Client.shared.fetch(API.Users.create(name: "John", age: 32)) { (result) in
   switch result {
   case .success(let user, let response):
       print(user)
       break
   case .failure(let error):
       print(error)
   }
}

Advanced

Here is a more advanced example with common use cases encountered while performing requests against an API.

import Foundation
import Htp

struct Pokemon: Decodable {
    let id: Int
    let name: String
    let height: Int
    let weight: Int
    let abilities: [Ability]                               // Handling of nested complex types.
    
    struct Ability: Decodable {
        let isHidden: Bool
        let slot: Int
        let ability: Detail
        
        struct Detail: Decodable {
            let name: String
            let url: URL
        }
        
        enum CodingKeys: String, CodingKey {
            case isHidden = "is_hidden"                    // Handling of custom property names while encoding/decoding
            case slot
            case ability
        }
    }
}

struct API {
        
    static func get(id: Int) -> Endpoint<Void, Pokemon> {
        return Endpoint(
            method: .get,
            path: "/pokemon/\(id)"
        )
    }
}

let client = Client(
    baseURL: URL(string: "https://pokeapi.co/api/v2")!,   // Set custom base URL.
    baseHeaders: ["X-API-Key": "xxxxx"]                   // Set custom default headers like API keys.
)

client.fetch(API.get(id: 1)) { (result) in
   switch result {
   case .success(let pokemon, let response):              // Catch decoded model and full response object.
       print(pokemon)
       break
   case .failure(let error):
       print(error)
   }
}

Contribution

Please, feel free to give feedback by opening an issue, or even better by forking and submitting a PR ! ;)