Contents

ersanq/networkkit

Clean async/await HTTP client for Swift. URLSession without the boilerplate.

The Problem

// 😭 Native URLSession β€” 20 lines for a simple GET
guard let url = URL(string: "https://api.example.com/users") else { return }
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return }
let users = try JSONDecoder().decode([User].self, from: data)

The Solution

// 😍 NetworkKit β€” 1 line
let users: [User] = try await Network.get("https://api.example.com/users")

Features

  • βœ… async/await native β€” no callbacks, no Combine
  • βœ… One-liners for GET, POST, PUT, PATCH, DELETE
  • βœ… Automatic JSON encoding/decoding
  • βœ… Smart error mapping β€” .unauthorized, .notFound, .noConnection
  • βœ… Fluent request builder β€” .header().body().query().timeout()
  • βœ… Configurable NetworkClient with base URL + default headers
  • βœ… Full NetworkResponse access when needed
  • βœ… Zero dependencies β€” wraps native URLSession
  • βœ… iOS 16+, macOS 13+, tvOS, watchOS, visionOS

Installation

https://github.com/ErsanQ/NetworkKit
.package(url: "https://github.com/ErsanQ/NetworkKit", from: "1.0.0")

Usage

Quick Requests

import NetworkKit

// GET
let users: [User] = try await Network.get("https://api.example.com/users")

// GET with query params
let results: SearchResult = try await Network.get(
    "https://api.example.com/search",
    query: ["q": "swift", "page": "1"]
)

// POST
let created: Post = try await Network.post("https://api.example.com/posts", body: newPost)

// PUT
let updated: User = try await Network.put("https://api.example.com/users/1", body: updatedUser)

// PATCH
let patched: User = try await Network.patch("https://api.example.com/users/1", body: changes)

// DELETE
try await Network.delete("https://api.example.com/posts/42")

Dedicated Client (Recommended for APIs)

// Configure once
let api = NetworkClient(baseURL: "https://api.example.com")
api.defaultHeaders["Authorization"] = "Bearer \(token)"
api.defaultHeaders["X-App-Version"] = "2.0"

// Use everywhere
let me: User           = try await api.get("/me")
let posts: [Post]      = try await api.get("/posts", query: ["page": "1"])
let new: Post          = try await api.post("/posts", body: newPost)
try await api.delete("/posts/42")

Error Handling

do {
    let user: User = try await api.get("/me")
} catch NetworkError.unauthorized {
    refreshToken()
} catch NetworkError.noConnection {
    showOfflineBanner()
} catch NetworkError.notFound {
    show404()
} catch NetworkError.serverError(let code) {
    logError(code)
} catch NetworkError.decodingFailed(let error) {
    print("Decode error:", error)
}

Custom Requests

let response = try await Network.response(for:
    NetworkRequest(url: "https://api.example.com/upload")
        .method(.post)
        .header("Authorization", value: "Bearer \(token)")
        .body(imageData)
        .timeout(120)
)

print(response.statusCode)
print(response.headers["ETag"] ?? "")
let result = try response.decode(UploadResult.self)

API Reference

Network (static)

| Method | Description | |--------|-------------| | get(:query:) | GET + decode | | post(:body:) | POST + decode or discard | | put(:body:) | PUT + decode | | patch(:body:) | PATCH + decode | | delete(_:) | DELETE | | response(for:) | Raw NetworkResponse |

NetworkClient

| Property | Description | |----------|-------------| | baseURL | Prepended to all relative paths | | defaultHeaders | Sent with every request | | defaultTimeout | Default: 30 seconds |

NetworkRequest (fluent builder)

.method() Β· .header() Β· .headers() Β· .body() Β· .query() Β· .timeout()

NetworkError

.noConnection Β· .unauthorized Β· .forbidden Β· .notFound Β· .serverError(statusCode:) Β· .decodingFailed(:) Β· .timeout Β· .invalidURL(:)


Requirements

  • iOS 16.0+ / macOS 13.0+ / tvOS 16.0+ / watchOS 9.0+ / visionOS 1.0+
  • Swift 5.9+
  • Xcode 15.0+

License

MIT License. See LICENSE.


Author

Built by Ersan Q Abo Esha β€” @ErsanQ

If NetworkKit saved you time, consider giving it a ⭐️ on GitHub.

Package Metadata

Repository: ersanq/networkkit

Default branch: main

README: README.md