Skip to content

A lightweight, modern Swift network layer built on Swift 6 concurrency and best practices.

License

Notifications You must be signed in to change notification settings

gentle-giraffe-apps/GentleNetworking

Repository files navigation

GentleNetworking

A lightweight, Swift-6-ready networking library designed for modern iOS apps using async/await, clean architecture, and testable abstractions.

Build Swift SPM Compatible Platform License

Coverage DeepSource Static Analysis DeepSource DeepSource

Codecov Snapshot
Codecov coverage chart

πŸ’¬ Join the discussion. Feedback and questions welcome


✨ Features

  • βœ… Native async/await API
  • βœ… Protocol-based, fully mockable networking layer
  • βœ… Typed request / response decoding
  • βœ… Swift 6 + Swift Concurrency friendly
  • βœ… Designed for MVVM / Clean Architecture
  • βœ… Zero third-party dependencies
  • βœ… Built-in canned response transports for testing

Demo App

A runnable SwiftUI demo app is included in this repository using a local package reference.

How to Run

  1. Clone the repository:
    git clone https://github.com/gentle-giraffe-apps/GentleNetworking.git
  2. Open the demo project:
    Demo/GentleNetworkingDemo/GentleNetworkingDemo.xcodeproj
    
  3. Select an iOS 17+ simulator.
  4. Build & Run (⌘R).

The project is preconfigured with a local Swift Package reference to GentleNetworking and should run without any additional setup.


πŸ“¦ Installation (Swift Package Manager)

Via Xcode

  1. Open your project in Xcode
  2. Go to File β†’ Add Packages...
  3. Enter the repository URL: https://github.com/gentle-giraffe-apps/GentleNetworking.git
  4. Choose a version rule (or main while developing)
  5. Add the GentleNetworking product to your app target

Quality & Tooling

This project enforces quality gates via CI and static analysis:

  • CI: All commits to main must pass GitHub Actions checks
  • Static analysis: DeepSource runs on every commit to main.
    The badge indicates the current number of outstanding static analysis issues.
  • Test coverage: Codecov reports line coverage for the main branch

These checks are intended to keep the design system safe to evolve over time.


Architecture

GentleNetworking is centered around a single, protocol-driven HTTPNetworkService that coordinates requests using injected endpoint, environment, and authentication abstractions.

flowchart TB
    HTTP["HTTPNetworkService<br/><br/>- request(...)"]

    Endpoint["EndpointProtocol<br/><br/><br/>"]
    Env["APIEnvironmentProtocol<br/><br/><br/>"]
    Auth["AuthServiceProtocol<br/><br/><br/>"]

    HTTP --> Endpoint
    HTTP --> Env
    HTTP -->|injected| Auth
Loading

Endpoint

flowchart TB
    APIEndpoint["APIEndpoint enum<br/><br/>case endpoint1<br/>…<br/>endpointN"]

    EndpointProtocol["EndpointProtocol<br/><br/>- path<br/>- method<br/>- query<br/>- body<br/>- requiresAuth"]

    APIEndpoint -->|conforms to| EndpointProtocol
Loading

πŸš€ Basic Usage

1. Define an API and Endpoints

import GentleNetworking

    let apiEnvironment = DefaultAPIEnvironment(
        baseURL: URL(string: "https://api.company.com")
    )

    nonisolated enum APIEndpoint: EndpointProtocol {
        case signIn(username: String, password: String)
        case model(id: Int)
        case models
    
        var path: String {
            switch self {
            case .signIn: "/api/signIn"
            case .model(let id): "/api/model/\(id)"
            case .models: "/api/models"
            }
        }

        var method: HTTPMethod {
            switch self {
            case .signIn: .post
            case .model, .models: .get
            }
        }

        var query: [URLQueryItem]? {
            switch self {
            case .signIn, .model, .models: nil
            }
        }

        var body: [String: EndpointAnyEncodable]? {
            switch self {
            case .signIn(let username, let password): [
                "username": EndpointAnyEncodable(username),
                "password": EndpointAnyEncodable(password)
            ]
            case .model, .models: nil
            }
        }
        
        var requiresAuth: Bool {
            switch self {
            case .model, .models: true
            case .signIn(username: _, password: _): false
            }
        }
    }

2. Create a Network Service

	let networkService = HTTPNetworkService()

3. Authenticate if Needed

    let keyChainAuthService = SystemKeyChainAuthService()

    struct AuthTokenModel: Decodable, Sendable {
        let token: String
    }

    let authTokenModel: AuthTokenModel = try await networkService.request(
        to: .signIn(username: "user", password: "pass"),
        via: apiEnvironment
    )

    try await keyChainAuthService.saveAccessToken(
        authTokenModel.token
    )

4. Request a Model

    struct Model: Decodable, Sendable {
        let id: Int
        let property: String
    }

	let model: Model = try await networkService.request(
        to: .model(id: 123),
        via: apiEnvironment
    )

5. Request an Array of Models

	let models: [Model] = try await networkService.requestModels(
        to: .models, 
        via: apiEnvironment
    )

πŸ§ͺ Testing

GentleNetworking provides a transport-layer abstraction for easy mocking in tests.

CannedResponseTransport

Returns a fixed response for any request:

let transport = CannedResponseTransport(
    string: #"{"id": 1, "title": "Test"}"#,
    statusCode: 200
)

let networkService = HTTPNetworkService(transport: transport)

CannedRoutesTransport

Match requests by method and path pattern for more realistic test scenarios:

let transport = CannedRoutesTransport(routes: [
    CannedRoute(
        pattern: RequestPattern(method: .get, path: "/api/models"),
        response: CannedResponse(string: #"[{"id": 1}]"#)
    ),
    CannedRoute(
        pattern: RequestPattern(method: .post, pathRegex: "^/api/model/\\d+$"),
        response: CannedResponse(string: #"{"success": true}"#)
    )
])

let networkService = HTTPNetworkService(transport: transport)

🧭 Design Philosophy

GentleNetworking is built around:

  • βœ… Predictability over magic
  • βœ… Protocol-driven design
  • βœ… Explicit dependency injection
  • βœ… Modern Swift concurrency
  • βœ… Testability by default
  • βœ… Small surface area with strong guarantees

It is intentionally minimal and avoids over-abstracting or hiding networking behavior.


πŸ€– Tooling Note

Portions of drafting and editorial refinement in this repository were accelerated using large language models (including ChatGPT, Claude, and Gemini) under direct human design, validation, and final approval. All technical decisions, code, and architectural conclusions are authored and verified by the repository maintainer.


πŸ” License

MIT License Free for personal and commercial use.


πŸ‘€ Author

Built by Jonathan Ritchey Gentle Giraffe Apps Senior iOS Engineer --- Swift | SwiftUI | Concurrency

Visitors

About

A lightweight, modern Swift network layer built on Swift 6 concurrency and best practices.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages