swift workflow

Legatus 🏇

Legatus(英语化为Legate)是罗马军队中高级别的罗马军官,相当于现代的高级将官。最初用于委托权力,该术语在奥古斯都统治下正式确定为军团的指挥官。 Legatus也是罗马共和国大使的术语,由参议院任命执行前往外国的任务(legatio),也用于从其他国家来到罗马的大使。

简介 🎬

Legatus 的基本思想是,我们需要一个网络抽象层,能够充分地封装直接调用 Alamofire 的行为。

此外,如果有一个与 SwiftUI 开箱即用的网络层 📦,岂不是很棒?🧐

幸运的是,Legatus 使用 Combine 框架实现,并具有一些奇特的方法,允许你将响应模型 assign(to:on:)@Published 属性。 太棒了!🤩

Legatus 的一些很棒的功能🌟

Legatus 的灵感来自 Moya

项目状态 🤖

我认为它已准备好用于生产环境。
欢迎任何贡献(拉取请求、问题、建议)!😃

要求 📝

安装 📦

你可以使用 Xcode SPM GUI:File -> Swift Packages -> Add Package Dependency -> 选择 "Up to Next Major Version 2.0.0"

或者将以下内容添加到你的 Package.swift 文件中

.package(url: "https://github.com/artemkalinovsky/Legatus.git", .upToNextMajor(from: Version("2.0.0")))

然后在你希望使用 Legatus 的 Target 中指定 "Legatus" 作为依赖项。 这是一个示例 PackageDescription

// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]),
    ],
    dependencies: [
        .package(url: "https://github.com/artemkalinovsky/Legatus.git", .upToNextMajor(from: Version("2.0.0")))
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: ["Legatus"])
    ]
)

基本用法 🧑‍💻

假设我们要从 JSON 获取用户列表,并且响应如下所示

{ 
   "results":[ 
      { 
         "name":{ 
            "first":"brad",
            "last":"gibson"
         },
         "email":"brad.gibson@example.com"
      }
   ]
}
  1. 创建 APIClient
    let apiClient = APIClient(baseURL: URL(string: "https://webservice.com/api/")!)
  1. 创建响应模型
import Foundation
import Legatus

final class User: Decodable {
    let firstName: String?
    let lastName: String?
    let email: String?

    enum CodingKeys: String, CodingKey {
        case name
        case email
    }

    enum NameKeys: String, CodingKey {
        case firstName = "first"
        case lastName = "last"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        email = try values.decodeIfPresent(String.self, forKey: .email)

        let name = try values.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        firstName = try name.decodeIfPresent(String.self, forKey: .firstName)
        lastName = try name.decodeIfPresent(String.self, forKey: .lastName)
    }
}
  1. 使用端点路径和所需的响应反序列化器创建请求
import Foundation
import Legatus

final class UsersApiRequest: DeserializeableRequest {

    var path: String {
        "users"
    }
    
    var deserializer: ResponseDeserializer<[User]> {
        JSONDeserializer<User>.collectionDeserializer(keyPath: "results")
    }

}
    apiClient.executeRequest(request: UsersApiRequest()) { result in }

瞧!🧑‍🎨

高级用法 🤓💻

要将响应直接反序列化到 CoreData NSManagedObject,请首先调用指定的初始化器

import Foundation
import CoreData
import Legatus

@objc(CoreDataObject)
public class CoreDataObject: NSManagedObject, Decodable {

    required convenience public init(from decoder: Decoder) throws {
        self.init(context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType))

        //TODO: implement decoding
    }

}

要将响应直接反序列化到 Realm Object 子类

import Foundation
import RealmSwift
import Legatus

final class RealmObject: Object, Decodable {

    @objc dynamic var name = ""

    required init() {
        super.init()
    }

    convenience init(from decoder: Decoder) throws {
        self.init()

        //TODO: implement decoding
    }
}

同样的功能也适用于 XMLDeserializer

如果要重试先前失败的请求,只需提供所需的重试次数

    apiClient.executeRequest(request: UsersApiRequest(), retries: 3) { result in }

要取消某个请求,你必须存储它的取消令牌并调用 cancel() 方法。

    let cancelationToken = apiClient.executeRequest(request: UsersApiRequest()) { result in }
    
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
            cancelationToken.cancel()
    }

此外,你可以取消所有活动请求

    apiClient.cancelAllRequests()

Combine 扩展 🚜

在使用 SwiftUI 时,其中大多数 UI 更新都基于底层的 Combine 机制,因此获取 Publisher 作为请求结果以进行未来的转换和赋值非常方便

    @Published var users = [User]()
    var subscriptions = Set<AnyCancellable>()

    apiClient
         .responsePublisher(request: UsersApiRequest())
         .catch { _ in return Just([User]())}
         .assign(to: \.users, on: self)
         .store(in: &subscriptions)

Swift Concurrency 扩展 🚦

    do {
        let usersResponse = try await apiClient.executeRequest(request: UsersApiRequest())
    } catch {
        // handle error
    }

使用 Legatus 的应用程序 📱

致谢 👏

许可证 📄

Legatus 在 MIT 许可证下发布。 有关更多信息,请参见 LICENCE