Spider-Web 2.1.0

Spider-Web 2.1.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最新发布2022年1月
SPM支持SPM

Mitch Treece维护。



Spider  Spider:Swift的Creepy网络库

Version Swift iOS License

Spider是一个易于使用的网络库,专为速度和可读性而构建。现代语法和响应处理使得与Web服务的工作如此简单 - 几乎像是在闹鬼。

安装

CocoaPods

Spider与CocoaPods集成!

  1. 将以下内容添加到您的Podfile中:
use_frameworks!
pod 'Spider-Web'
  1. 在您的项目目录中,运行pod install
  2. Spider模块导入到您需要它的任何地方
  3. 就能大赚一把

手动安装

您还可以手动将源文件添加到您的项目中。

  1. 克隆此git仓库
  2. 将《Spider/》子目录中所有的Swift文件添加到您的项目
  3. 就能大赚一把

基础知识

Spider可以用多种方式使用。大多数时候,共享的Spider实例就足够了。

Spider.web
    .get("https://path/to/endpoint")
    .data { _ in
        print("We got a response!")
    }

这将以给定的路径发起一个GET请求,然后返回一个Response对象。

基本URL

因为我们通常会对某个API发起多次请求,所以使用基本URL是非常合理的。当我们需要在API的不同版本之间切换时(例如,开发、预生产、生产等),这也非常有用。

Spider.web.baseUrl = "https://api.spider.com/v1"

Spider.web
    .get("/users")
    .data { _ in
        print("We got a response!")
    }

Spider.web
    .get("/locations")
    .data { _ in
        print("We got another response!")
    }

请注意,我们如何使用相同的共享基本URL来向特定端点发起请求。上述请求将撞击以下端点

https://base.url/v1/users
https://base.url/v1/locations

如果没有指定基本URL,Spider会假设请求的path是完整的URL(第一例中可以看到)。

请求参数

所有代码的请求实例化方式都提供了一种方法,允许你传入请求参数。例如

let params = ["user_id": "123456789"]

Spider.web
    .post(
        "https://path/to/endpoint", 
        parameters: params
    )
    .data { _ in
        print("We got a response!")
    }

这将接收你的参数,并将它们通过请求体传递。对于GET请求,参数将被编码到路径中作为查询参数。

Spider实例

迄今为止,我们一直在使用共享的Spider实例。这通常已经足够了。如果你需要更多的控制权,Spider还支持更传统的实例化流程。

let tarantula = Spider()

tarantula
    .get("https://path/to/endpoint")
    .data { _ in
        print("Tarantula got a response!")
    }

不是使用共享的Spider实例,我们创建了名为tarantuala的自己的实例并使用它进行请求。很可怕!自然地,这样创建的Spider实例也支持基本URL

let blackWidow = Spider(baseUrl: "https://base.url/v1")

blackWidow
    .get("/users")
    .data { _ in
        print("Black Widow got a response!")
    }

高级 & 多部分请求

Spider 也支持更精细的请求选项。您可以手动配置和执行 请求

let request = Request(
    method: .get,
    path: "https://path/to/endpoint",
    parameters: nil
)

request.header.accept = [
    .image_jpeg,
    .custom("custom_accept_type")
]

request.header.set(
    value: "12345",
    forHeaderField: "user_id"
)

Spider.web
    .perform(request)
    .data { _ in
        print("We got a response!")
    }

多部分请求也可以以类似的方式构建和执行

let file = MultipartFile(
    data: image.pngData()!,
    key: "image",
    name: "image.png",
    type: .image_png
)

let request = MultipartRequest(
    method: .put,
    path: "https://path/to/upload",
    parameters: nil,
    files: [file]
)

Spider.web
    .perform(request)
    .data { _ in
        print("We got a response!")
    }

MultipartRequest 是一个使用一组 MultipartFile 对象初始化的 Request 子类。其他一切与普通请求完全相同。

授权

目前,Spider 支持以下授权类型

  • 基本类型 (用户:pass base64 编码)
  • Bearer 令牌

授权可以逐个请求或基于实例添加。通常,我们希望为 Spider 实例提供授权,以便所有请求都带此授权发送

let authSpider = Spider(
    baseUrl: "https://base.url/v1",
    authorization: TokenRequestAuth(value: "0123456789")
)

authSpider
    .get("/topSecretData")
    .data { _ in
        print("Big hairy spider got a response!")
    }

但是,如果更适合您的需求,授权也可以按请求提供

let token = TokenRequestAuth(value: "0123456789")
let spider = Spider(baseUrl: "https://base.url/v1")

spider
    .get(
        "/topSecretData", 
        authorization: token
    )
    .data { _ in
        print("Spider got a response!")
    }

高级请求也可以提供授权

let request = Request(
    method: .get,
    path: "https://path/to/endpoint",
    authorization: TokenAuth(value: "0123456789")
)

request.header.accept = [
    .image_jpeg,
    .custom("custom_accept_type")
]

request.header.set(
    value: "12345",
    forHeaderField: "user_id"
)

Spider.web
    .perform(request)
    .data { _ in
        print("We got a response!")
    }

默认情况下,授权添加到 "Authorization" 头字段。创建授权时可以通过传递自定义字段来更改

let basic = BasicRequestAuth(
    username: "root",
    password: "pa55w0rd",
    field: "Credentials"
)

let authSpider = Spider(
    baseUrl: "https://base.url/v1",
    authorization: basic
)

authSpider
    .get("/topSecretData")
    .data { _ in
        print("Spider got a response!")
    }

如果需要,也可以自定义授权 前缀。例如,BasicRequestAuth 为 "root:pa55w0rd" 凭据生成以下内容

Basic cm9vdDpwYTU1dzByZA==

在这种情况下,在编码凭据之前的 "Basic" 前缀是授权的 类型。可以如下自定义

let basic = BasicRequestAuth(
    username: "root",
    password: "pa55w0rd"
)

basic.prefix = "Login"

let spider = Spider(
    baseUrl: "https://base.url/v1",
    authorization: basic
)

spider
    .get("/topSecretData")
    .data { _ in
        print("Got a response!")
    }

类似地,可以以相同的方式修改 TokenRequestAuth"Bearer" 前缀。

响应

响应 对象干净、易于使用。典型数据响应可能看起来像以下这样

Spider.web
    .get("https://some/data/endpoint")
    .dataResponse { res in

        switch res.result {
        case .success(let data): // Handle response data
        case .failure(let error): // Handle response error
        }

    }

响应 还具有辅助 valueerror 属性,如果您喜欢这种语法

Spider.web
    .get("https://some/data/endpoint")
    .dataResponse { res in

        if let error = res.error {
            // Handle the error
            return
        }

        guard let data = res.value else {
            // Missing data
            return
        }

        // Do something with the response data

    }

工作线程 & 序列化

当被要求执行请求时,Spider 创建并返回一个 RequestWorker 实例。工作线程实际上是管理请求执行和响应序列化的组件。例如,上面的示例可以分解如下:

let worker = Spider.web
    .get("https://some/data/endpoint")

worker.dataResponse { res in

    if let error = res.error {
        // Handle the error
        return
    }

    guard let data = res.value else {
        // Missing data
        return
    }

    // Do something with the response data

}

如果您更愿意直接处理响应的值而不是响应本身,每个工作函数都提供了原始值替代方案。

let worker = Spider.web
    .get("https://some/data/endpoint")

worker.data { (data, error) in

    if let error = error {
        // Handle the error
        return
    }

    guard let data = data else {
        // Missing data
        return
    }

    // Do something with the data

}

除了Data之外,RequestWorker还支持以下序列化函数。

func stringResponse(encoding: String.Encoding, completion: ...)
func string(encoding: String.Encoding, completion: ...)

func jsonResponse(completion: ...)
func json(completion: ...)
func jsonArrayResponse(completion: ...)
func jsonArray(completion: ...)

func imageResponse(completion:)
func image(completion: ...)

func decodeResponse<T: Decodable>(type: T.Type, completion: ...)
func decode<T: Decodable>(type: T.Type, completion: ...)

可以通过扩展RequestWorker来添加自定义序列化函数。

中间件

响应还可以通过中间件进行运行,以便在需要时验证或转换返回的数据。

class ExampleMiddleware: Middleware {

  override func next(_ response: Response<Data>) throws -> Response<Data> {

    let stringResponse = response.compactMap { $0.stringResponse() }

    switch stringResponse.result {
    case .success(let string):

      guard !string.isEmpty else {

        throw NSError(
          domain: "com.example.Spider-Example",
          code: -1,
          userInfo: [NSLocalizedDescriptionKey: "ExampleMiddleware failed"]
        )

      }

    case .failure(let error): throw error
    }

    return response

  }

}
Spider.web.middlewares = [ExampleMiddleware()]

Spider.web
    .get("https://path/to/endpoint")
    .data { _ in
        print("We got a response!")
    }

现在,通过共享的Spider实例执行的每个请求都会在将其传递给请求完成闭包之前先通过我们的ExampleMiddleware。中间件也可以按请求设置。

let request = Request(
    method: .get,
    path: "https://path/to/endpoint"
)

request.middlewares = [ExampleMiddleware()]

Spider.web
    .perform(request)
    .data { ... }

图片

通过SpiderImageDownloaderSpiderImageCache支持图片下载和缓存。Spider使用优秀的Kingfisher库来管理后台的图片下载和缓存。

SpiderImageDownloader

使用SpiderImageDownloader下载图片非常简单!

SpiderImageDownloader.getImage("http://url.to/image.png") { (image, isFromCache, error) in

    guard let image = image, error == nil else {
        // Handle error
    }

    // Do something with the image

}

上面所示的getImage()函数返回一个可取消的任务,可用于在需要时取消下载。

let task = SpiderImageDownloader.getImage("http://url.to/image.png") { (image, isCachedImage, error) in
    ...
}

task.cancel()

默认情况下,SpiderImageDownloader不缓存已下载的图片。如果您想要缓存图片,只需在调用getImage()函数时将cache标志设置为true即可。

SpiderImageCache

缓存、获取和从缓存中删除图片

let imageCache = SpiderImageCache.shared
let image: UIImage = ...
let key = "my_image_key"

// Add an image to the cache
imageCache.cache(image, forKey: key) { ... }

// Fetch an image from the cache
if let image = imageCache.image(forKey: key) { ... }

// Remove an image from the cache
imageCache.removeImage(forKey: key) { ... }

您还可以清理缓存

// Clean the disk cache
imageCache.clean(.disk)

// Clean the memory cache
imageCache.clean(.memory)

// Clean all caches
imageCache.cleanAll()

UI 集成

蜘蛛也提供了一些巧妙的 UI 集成,例如图像视图加载!

imageView.web.setImage("http://url.to/image.png")

目前,蜘蛛已针对以下 UI 元素进行了集成

  • UIImageView / NSImageView

Async / Await

自 Swift 5.5 开始,async/await 已内置到标准库中!如果你针对 iOS 13 或 macOS 12,可以使用蜘蛛的异步工作器变体。

let photo = await Spider.web
    .get("https://jsonplaceholder.typicode.com/photos/1")
    .decode(Photo.self)

guard let photo = photo else { return }

let image = await Spider.web
    .get(photo.url)
    .image()

if let image = image {

    // Do something with the image!

}

Promise

蜘蛛支持内置 PromiseKit。Promise 帮助你保持代码库的整洁易读,通过消除恼人的嵌套回调。

Spider.web
    .get("https://jsonplaceholder.typicode.com/photos/1")
    .decode(Photo.self)
    .then { photo -> Promise<Image> in

        return Spider.web
            .get(photo.url)
            .image()

    }
    .done { image in

        // Do something with the image!

    }
    .catch { error in

        // Handle error

    }

Async / Await

自 Swift 5.5 开始,async/await 已内置到标准库中!如果你针对 iOS 15 或 macOS 12,可以使用蜘蛛的异步工作器变体。

do {

    let photo = try await Spider.web
        .get("https://jsonplaceholder.typicode.com/photos/1")
        .decode(Photo.self)

    let image = try await Spider.web
        .get(photo.url)
        .image()

    // Do something with the image!

}
catch {

    // Handle error

}

调试模式

启用蜘蛛的 isDebugEnabled 标志将在控制台打印所有调试信息(包括所有出站请求)。

贡献

欢迎提交拉取请求。是修复bug还是添加特性?提交一个PR,我们会将其合并的!