SwiftApiAdapter

SwiftApiAdapter 是一个 Swift 包,旨在简化 Swift 应用程序中检索远程内容(如文本和图像)的过程。它提供了一个强大的框架来管理 API 连接器、异步处理请求和处理响应,重点是效率和有效性。此包尤其适用于调用生成式 AI API 和加载网页内容。

特性

关于 GET 请求的重要提示

为了符合标准 HTTP 用法,如果 HTTP 方法是 GET,SwiftApiAdapter 不会附加任何请求体。如果您的配置指定了 GET 以及非空的主体,则该主体将被忽略。如果您需要发送有效负载,请切换到 POSTPUT 等方法。

安装

Swift Package Manager

通过 Swift Package Manager 将 SwiftApiAdapter 添加到您的项目中,方法是在您的 Package.swift 中包含它。

dependencies: [
    .package(url: "https://github.com/RayKitajima/SwiftApiAdapter.git", from: "1.0.0")
]

确保存储库 URL 与您的 Swift 包的位置匹配。

用法

导入包

在需要 API 处理的 Swift 文件中导入 SwiftApiAdapter

import SwiftApiAdapter

用法示例

发送带有自定义头的 API 请求

  1. 获取 ApiConnector:
let apiConnector = ApiConnectorManager.shared.getConnector(for: "ExampleAPI")
  1. 发送带有自定义头的 API 请求,包括 User-Agent
let endpoint = URL(string: "https://example.com/api")!
let headers = [
    "User-Agent": "Your Custom User-Agent",
    "Content-Type": "application/json"
]
let response = await apiConnector.requester.processJsonApi(
    endpoint: endpoint,
    method: "GET",
    headers: headers,
    body: "" // Empty body for GET request; otherwise body is ignored
)

使用 ApiContentLoader 加载 API 内容

此示例演示如何使用 ApiContentLoader 将特定数据从 API 响应加载到您的应用程序中。该示例获取嵌套在 "data" JSON 对象中的键为 "result" 的值。

  1. 定义 ApiContent:

    通过指定端点、HTTP 方法、头部以及从 API 响应中提取特定数据所需的任何参数来配置您的 API 内容。您还可以使用 extraData 字段添加管理 API 所需的任何额外数据。

let apiContent = ApiContent(
    id: UUID(),
    name: "Example API Content",
    endpoint: "https://exampleapi.com/data",
    method: .get,
    headers: ["Authorization": "Bearer your_access_token"],
    body: "",  // No body needed for GET request
    arguments: ["result": "[\"data\"][\"result\"]"],  // Path to extract `result` from the JSON response
    extraData: ["info": "additional info"]  // Additional data
)
  1. 使用 ApiContentLoader 加载内容:

    使用 ApiContentLoader.load 方法进行 API 调用并处理响应,根据提供的路径提取 result 值。

do {
    let apiContentRack = try await ApiContentLoader.load(
        contextId: UUID(),  // Context ID to uniquely identify this load operation
        apiContent: apiContent
    )
    if let apiContentRack = apiContentRack {
        let resultValue = apiContentRack.arguments["result"] ?? "No result found"
        print("API Data Loaded: \(resultValue)")
        if let extraInfo = apiContent.extraData?["info"] as? String {
            print("Extra Info: \(extraInfo)")
        }
    } else {
        print("Failed to load API data.")
    }
} catch {
    print("An error occurred: \(error)")
}

加载网页内容

您还可以使用 ApiContentLoader 使用相同的接口加载和处理网页内容。

  1. 定义网页的 ApiContent
let apiContentPage = ApiContent(
    id: UUID(),
    name: "Web Page Content",
    endpoint: "https://example.com/page",
    method: .get,
    headers: ["Authorization": "Bearer your_access_token"],
    body: "",  // No body needed for GET request
    contentType: .page
)
  1. 使用 ApiContentLoader 加载网页内容:

    使用 ApiContentLoader.load 方法发出请求并处理网页内容。contenturlogimagefinalUrl 字段会自动提取并设置为 ApiContentRack

do {
    let apiContentRack = try await ApiContentLoader.load(
        contextId: UUID(),  // Context ID to uniquely identify this load operation
        apiContent: apiContentPage
    )
    if let apiContentRack = apiContentRack {
        let content = apiContentRack.arguments["content"] ?? "No content found"
        let url = apiContentRack.arguments["url"] ?? "No URL found"
        let ogimage = apiContentRack.arguments["ogimage"] ?? "No image found"
        let finalUrl = apiContentRack.arguments["finalUrl"] ?? "Actual endpoint" 
        print("Web Page Content Loaded: \(content)")
        print("URL: \(url)")
        print("OpenGraph Image: \(ogimage)")
        print("Actual endpoint: \(finalUrl)")
    } else {
        print("Failed to load web page content.")
    }
} catch {
    print("An error occurred: \(error)")
}

与 SwiftUI 集成

以下示例演示了如何将 SwiftApiAdapter 与 SwiftUI 集成,从而使您能够根据 API 请求计数和其他动态数据反应式地更新 UI。

控制器

创建一个控制器,用于管理 API 交互并更新可观察属性

import SwiftUI

class ApiController: NSObject, ObservableObject {
    @Published var cumulativeRequested: Int = 0
    @Published var cumulativeExecuted: Int = 0

    var apiConnector: ApiConnector?

    func generate() async throws {
        let connector = ApiConnectorManager.shared.getConnector(for: "ExampleAPI")
        self.apiConnector = connector
        connector.initTransaction()

        DispatchQueue.main.async {
            self.apiConnector?.executor.$cumulativeRequested.assign(to: &self.$cumulativeRequested)
            self.apiConnector?.executor.$cumulativeExecuted.assign(to: &self.$cumulativeExecuted)
        }

        connector.executor.start()
    }
}
视图

利用 SwiftUI 视图来反映 API 操作的状态

import SwiftUI

struct ApiView: View {
    @ObservedObject var apiController: ApiController

    var body: some View {
        HStack(spacing: 5) {
            Text("Generating")
            Text("(\(apiController.cumulativeExecuted)/\(apiController.cumulativeRequested))")
                .foregroundColor(.secondary)
            Image(systemName: "ellipsis")
                .animation(.easeInOut, value: apiController.cumulativeExecuted)
        }
    }
}

使用 ApiContentLoader 在 SwiftUI 中加载 API 内容

SwiftUI 可以根据从 API 获取的数据动态更新视图。下面,我们将演示如何在 SwiftUI 视图中使用 ApiContentLoader 来加载和显示 base64 编码的图像和文本内容。

示例 1:显示 Base64 编码的图像
  1. 定义 base64 图像的 ApiContent
let apiContentImage = ApiContent(
    id: UUID(),
    name: "Image API Content",
    endpoint: "https://exampleapi.com/image",
    method: .get,
    headers: ["Authorization": "Bearer your_access_token"],
    body: "",
    arguments: ["image": "[\"data\"][0][\"message\"][\"content\"]"],
    contentType: .base64image,
    extraData: ["info": "additional image info"]
)
  1. SwiftUI 视图以显示图像
import SwiftUI

struct ImageView: View {
    @ObservedObject var apiController: ApiController

    var body: some View {
        VStack {
            if let imageData = apiController.imageData {
                Image(uiImage: UIImage(data: imageData)!)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } else {
                Text("Loading Image...")
            }
        }
        .onAppear {
            Task {
                await apiController.loadImageContent()
            }
        }
    }
}
  1. ApiController 加载图像内容:
import Combine

class ApiController: ObservableObject {
    @Published var imageData: Data?

    func loadImageContent() async {
        let apiContentImage = ApiContent(
            id: UUID(),
            name: "Image API Content",
            endpoint: "https://exampleapi.com/image",
            method: .get,
            headers: ["Authorization": "Bearer your_access_token"],
            body: "",
            arguments: ["image": "[\"data\"][0][\"message\"][\"content\"]"],
            contentType: .base64image
        )

        do {
            let apiContentRack = try await ApiContentLoader.load(
                contextId: UUID(),
                apiContent: apiContentImage
            )
            if let apiContentRack = apiContentRack,
               let base64String = apiContentRack.arguments["image"],
               let imageData = Data(base64Encoded: base64String) {
                DispatchQueue.main.async {
                    self.imageData = imageData
                }
            }
        } catch {
            print("Error loading image: \(error)")
        }
    }
}
示例 2:显示文本内容
  1. 定义文本的 ApiContent
let apiContentText = ApiContent(
    id: UUID(),
    name: "Text API Content",
    endpoint: "https://exampleapi.com/text",
    method: .get,
    headers: ["Authorization": "Bearer your_access_token"],
    body: "",
    arguments: ["text": "[\"choices\"][0][\"message\"][\"content\"]"],
    contentType: .text,
    extraData: ["info": "additional text info"]
)
  1. SwiftUI 视图以显示文本
import SwiftUI

struct TextView: View {
    @ObservedObject var apiController: ApiController

    var body: some View {
        Text(apiController.textData ?? "Loading Text...")
            .onAppear {
                Task {
                    await apiController.loadTextContent()
                }
            }
    }
}
  1. ApiController 加载文本内容:
import Combine

class ApiController: ObservableObject {
    @Published var textData: String?

    func loadTextContent() async {
        let apiContentText = ApiContent(
            id: UUID(),
            name: "Text API Content",
            endpoint: "https://exampleapi.com/text",
            method: .get,
            headers: ["Authorization": "Bearer your_access_token"],
            body: "",
            arguments: ["text": "[\"choices\"][0][\"message\"][\"content\"]"],
            contentType: .text
        )

        do {
            let apiContentRack = try await ApiContentLoader.load(
                contextId: UUID(),
                apiContent: apiContentText
            )
            if let apiContentRack = apiContentRack,
               let text = apiContentRack.arguments["text"] {
                DispatchQueue.main.async {
                    self.textData = text
                }
            }
        } catch {
            print("Error loading text: \(error)")
        }
    }
}

这些示例说明了如何在 SwiftUI 应用程序中反应式地获取和显示 API 内容。它们展示了一种在视图出现时启动 API 请求并根据结果更新视图状态的模式,从而将网络操作无缝集成到 SwiftUI 生命周期中。

管理 API 连接器

let connector = ApiConnectorManager.shared.getConnector(for: "Tag")
ApiConnectorManager.shared.clearConnector(for: "Tag")
ApiConnectorManager.shared.clearAllConnectors()

配置

您可以自定义每个 API 请求的头部,从而可以根据端点要求设置 User-Agent 和其他必要的头部。但是,请记住,对于 GET 请求,任何提供的主体数据都将被忽略

贡献

我们鼓励贡献!请随时提交拉取请求或打开问题以建议功能、改进或错误修复。