TinyNetworking

这个软件包包含一个微型的网络库。它提供了一个结构体 Endpoint,它结合了 URL 请求和解析该请求响应的方法。因为 Endpoint 对解析结果是泛型的,所以它提供了一种类型安全的方式来使用 HTTP 端点。

这里有一些例子

一个简单的端点

这是一个代表用户数据的端点 (请注意,JSON 中还有更多字段,为了简洁起见已省略)

struct User: Codable {
    var name: String
    var location: String?
}

func userInfo(login: String) -> Endpoint<User> {
    return Endpoint(json: .get, url: URL(string: "https://api.github.com/users/\(login)")!)
}

let sample = userInfo(login: "objcio")

上面的代码只是一个端点的描述,它不会加载任何东西。 sample 是一个简单的结构体,你可以检查它(例如,在单元测试中)。

这是如何加载一个端点的方法。 result 的类型是 Result<User, Error>

URLSession.shared.load(endpoint) { result in
   print(result)
}

或者,你可以使用 async/await 选项。

let result = try await URLSession.shared.load(endpoint)

身份验证端点

这是一个关于如何拥有身份验证端点的例子。你使用 API 密钥初始化 Mailchimp 结构体,并使用它来计算 authHeader。然后,在创建端点时,你可以使用 authHeader

struct Mailchimp {
    let base = URL(string: "https://us7.api.mailchimp.com/3.0/")!
    var apiKey = env.mailchimpApiKey
    var authHeader: [String: String] { 
        ["Authorization": "Basic " + "anystring:\(apiKey)".base64Encoded] 
    }

    func addContent(for episode: Episode, toCampaign campaignId: String) -> Endpoint<()> {
        struct Edit: Codable {
            var plain_text: String
            var html: String
        }
        let body = Edit(plain_text: plainText(episode), html: html(episode))
        let url = base.appendingPathComponent("campaigns/\(campaignId)/content")
        return Endpoint<()>(json: .put, url: url, body: body, headers: authHeader)
    }
}

自定义解析

JSON 编码和解码作为条件扩展添加到 Codable 基础设施之上。然而,Endpoint 本身与此完全无关。这是解析函数的类型

var parse: (Data?, URLResponse?) -> Result<A, Error>

Data 作为输入意味着你可以在其之上编写自己的功能。例如,这是一个解析图像的资源

struct ImageError: Error {}

extension Endpoint where A == UIImage {
    init(imageURL: URL) {
        self = Endpoint(.get, url: imageURL) { data in
            Result {
                guard let d = data, let i = UIImage(data: d) else { throw ImageError() }
                return i
            }
        }
    }
}

你还可以编写执行自定义 JSON 序列化、解析 XML 或其他格式的扩展。

测试端点

因为 Endpoint 是一个简单的结构体,所以很容易在没有网络连接的情况下同步测试。例如,你可以像这样测试图像端点

XCTAssertThrows(try Endpoint(imageURL: someURL).parse(nil, nil).get())
XCTAssertThrows(try Endpoint(imageURL: someURL).parse(invalidData, nil).get())
XCTAssertNoThrow(try Endpoint(imageURL: someURL).parse(validData, nil).get())

Combine

Hsieh Min Che 创建了一个库,为这个库添加了 Combine 端点: https://github.com/Hsieh-1989/CombinedEndpoint

更多例子

更多文档

这个库的设计和实现已经在 Swift Talk 上进行了广泛的介绍。 还有一个包含所有相关节目的合集。

网络