Back to OSS

swift-api-client

async/await対応の軽量HTTPクライアント - 型安全なAPI通信をSwiftで実現

Swift
http-clientasync-awaitnetworking

swift-api-client

モダンな async/await をサポートした軽量な Swift 製 HTTP API クライアントパッケージ

Swift Platforms SPM License

📚 完全なドキュメント

概要

swift-api-client は、Swift アプリケーションで HTTP API 呼び出しをシンプルかつ型安全に行うためのパッケージです。iOS および macOS プラットフォームに対応し、モダンな並行処理機能をサポートしています。

主な機能

  • モダンな async/await API - Swift 6.0 の並行処理機能をフル活用
  • 型安全なリクエスト/レスポンス - コンパイル時の型チェックで安全性を保証
  • 自動 JSON デコーディング - Codable を使った簡単なレスポンス処理
  • 柔軟なエラーハンドリング - 詳細なエラー情報を提供
  • 認証サポート - トークンプロバイダーによる認証統合
  • HTTP イベントストリーム - 認証エラー・レート制限等の重要イベントをAsyncStreamで通知
  • HTTP ログストリーム - 全リクエスト/レスポンスをAsyncStreamで監視可能
  • ゼロ依存 - 外部ライブラリを使わない軽量設計
  • クロスプラットフォーム - iOS および macOS 対応

必要要件

  • iOS 17.0+
  • macOS 14.0+
  • Swift 6.0+

インストール

Swift Package Manager

Package.swift に以下を追加してください:

dependencies: [
    .package(url: "https://github.com/no-problem-dev/swift-api-client.git", from: "1.0.0")
]

または Xcode で:

  1. File > Add Package Dependencies
  2. パッケージ URL を入力: https://github.com/no-problem-dev/swift-api-client.git
  3. バージョンを選択: 1.0.0 以降

クイックスタート

最もシンプルな使用例:

import APIClient

// API クライアントを作成
let client = APIClientImpl(baseURL: URL(string: "https://api.example.com")!)

// エンドポイントを定義
let endpoint = APIEndpoint(
    path: "/users/123",
    method: .get
)

// リクエストを実行
let data = try await client.request(endpoint)

使い方

基本的な GET リクエスト

import APIClient

// レスポンス型を定義
struct User: Codable {
    let id: String
    let name: String
    let email: String
}

// API クライアントを作成
let client = APIClientImpl(baseURL: URL(string: "https://api.example.com")!)

// エンドポイントを定義
let endpoint = APIEndpoint(
    path: "/users/123",
    method: .get
)

// リクエストを実行してデコード
let user: User = try await client.request(endpoint)
print(user.name)

POST リクエスト(JSON ボディ付き)

struct CreateUserRequest: Codable {
    let name: String
    let email: String
}

let requestBody = CreateUserRequest(name: "John Doe", email: "john@example.com")
let jsonData = try JSONEncoder().encode(requestBody)

let endpoint = APIEndpoint(
    path: "/users",
    method: .post,
    headers: ["Content-Type": "application/json"],
    body: jsonData
)

let newUser: User = try await client.request(endpoint)

クエリパラメータ付きリクエスト

let endpoint = APIEndpoint(
    path: "/users",
    method: .get,
    queryItems: [
        URLQueryItem(name: "page", value: "1"),
        URLQueryItem(name: "limit", value: "20")
    ]
)

let users: [User] = try await client.request(endpoint)

エラーハンドリング

do {
    let user: User = try await client.request(endpoint)
    print("Success: \(user.name)")
} catch APIError.unauthorized {
    print("認証エラー")
} catch APIError.networkError(let error) {
    print("ネットワークエラー: \(error.localizedDescription)")
} catch APIError.httpError(let statusCode, _) {
    print("HTTP エラー: \(statusCode)")
} catch APIError.decodingError(let error) {
    print("デコードエラー: \(error.localizedDescription)")
} catch {
    print("予期しないエラー: \(error)")
}

認証トークンの使用

// トークンプロバイダーを実装
class MyTokenProvider: AuthTokenProvider {
    func getToken() async throws -> String? {
        // トークン取得ロジック(例:Keychain から取得)
        return "your-auth-token"
    }
}

// クライアント作成時にトークンプロバイダーを指定
let client = APIClientImpl(
    baseURL: URL(string: "https://api.example.com")!,
    authTokenProvider: MyTokenProvider()
)

// リクエスト時に自動的に Authorization ヘッダーが追加されます
let user: User = try await client.request(endpoint)

HTTP イベントの監視

重要なHTTPレスポンス(401, 403, 429, 503, 5xx)をアプリ全体で監視できます。

// 認証エラーやサービス停止を一元的にハンドリング
Task {
    for await event in await client.events {
        switch event {
        case .unauthorized:
            await authManager.handleLogout()
        case .rateLimited(_, let retryAfter, _):
            print("レート制限: \(retryAfter ?? 0)秒後にリトライ")
        case .serviceUnavailable:
            await router.showMaintenanceScreen()
        default:
            break
        }
    }
}

HTTP ログの監視

全リクエスト/レスポンスをAsyncStreamで監視できます。

// デバッグ用コンソール出力(CustomStringConvertibleによる整形済み出力)
Task {
    for await log in await client.logs {
        print(log)  // 自動的に整形されたログが出力されます
    }
}

// カスタム処理(Analytics送信など)
Task {
    for await log in await client.logs {
        switch log {
        case .success(let endpoint, let statusCode, _):
            analytics.trackSuccess(endpoint: endpoint.path, statusCode: statusCode)
        case .httpError(let endpoint, let statusCode, _):
            analytics.trackError(endpoint: endpoint.path, statusCode: statusCode)
        case .decodingError(let endpoint, _, _, let targetType):
            analytics.trackDecodingError(endpoint: endpoint.path, type: targetType)
        }
    }
}

ライセンス

このプロジェクトは MIT ライセンスの下で公開されています。詳細は LICENSE ファイルをご覧ください。

サポート

問題が発生した場合や機能リクエストがある場合は、GitHub の Issue を作成してください。

© 2026 Kyoichi Taniguchi. All rights reserved.