TRON 5.5.0-beta.1

TRON 5.5.0-beta.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
Released最后发布2022年7月
SPM支持 SPM

Denys Telezhkin 维护。



TRON 5.5.0-beta.1

CI codecov.io CocoaPod platform CocoaPod version Swift Package Manager compatible Packagist

TRON 是一个基于 Alamofire 的轻量级网络抽象层。它可以用来极大地简化与 RESTful JSON 网络服务交互。

特性

  • 基于协议的泛型实现
  • 内置响应和错误解析
  • 支持自定义映射器(默认使用 `SwiftyJSON` 实现)。默认使用 `Codable` 协议。
  • 支持上传任务
  • 支持下载任务和断点续传
  • 优秀的插件系统
  • 模拟网络请求
  • 模块化架构
  • 支持 iOS/Mac OS X/tvOS/watchOS/Linux
  • 支持 CocoaPods/Swift Package Manager
  • RxSwift / Combine 扩展
  • 支持 `Swift Concurrency`
  • 完整的文档

概览

我们设计 TRON 以便于使用且易于定制。经过初始设置后,使用 TRON 非常简单

let request: APIRequest<User,APIError> = tron.codable.request("me")
request.perform(withSuccess: { user in
  print("Received User: \(user)")
}, failure: { error in
  print("User request failed, parsed error: \(error)")
})

要求

  • Xcode 13及以上
  • Swift 5.3及以上
  • iOS 11 / macOS 10.13 / tvOS 11.0 / watchOS 4.0

安装

Swift 包管理器

  • 在项目设置中添加包 -> Swift 包

TRON 框架包含 Codable 实现是为了使用 SwiftyJSON,请导入 TRONSwiftyJSON 框架。使用 RxSwift 封装请使用 RxTRON

CocoaPods

pod 'TRON', '~> 5.3.0'

仅包含 Core 子规格,不携带 SwiftyJSON 依赖

pod 'TRON/Core', '~> 5.3.0'

TRON 的 RxSwift 扩展

pod 'TRON/RxSwift', '~> 5.3.0'

迁移指南

项目状态

TRON 在 MLSDev Inc. 的积极开发下。欢迎 Pull 请求!

请求构建

TRON 对象作为 APIRequest 的初始配置器,设置所有基本值,并配置使用 baseURL。

let tron = TRON(baseURL: "https://api.myapp.com/")

您需要保持对 TRON 对象的强引用,因为它持有了运行所有请求的 Alamofire.Manager。

URLBuildable

URLBuildable 协议用于将相对路径转换为 URL,该 URL 会被请求使用。

public protocol URLBuildable {
    func url(forPath path: String) -> URL
}

默认情况下,TRON 使用 URLBuilder 类,该类简单地将相对路径附加到基本 URL 上,这对于大多数情况来说是足够的。您可以通过更改 TRON 上的 urlBuilder 属性或局部修改 APIRequest 上的 urlBuilder 属性来全局地自定义 URL 构建过程,以单个请求为范围。

发送请求

要发送 APIRequest,请调用 APIRequest 上的 perform(withSuccess:failure:) 方法。

let alamofireRequest = request.perform(withSuccess: { result in }, failure: { error in})

或者,您可以使用包含 Alamofire.ResponseperformCollectingTimeline(withCompletion:) 方法。

request.performCollectingTimeline(withCompletion: { response in
    print(response.timeline)
    print(response.result)
})

在这两种情况下,如果您需要,还可以链式调用 Alamofire.Request 方法。

request.perform(withSuccess: { result in }, failure: { error in }).progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
    print(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
}

响应解析

通用的 APIRequest 实现允许我们在请求甚至发送之前就定义预期的响应类型。在我们的 Alamofire DataResponseSerializerProtocol 的基础上,我们添加了一个额外的协议来处理错误。

public protocol DataResponseSerializerProtocol {
    associatedtype SerializedObject

    public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Self.SerializedObject
}

public protocol ErrorSerializable: Error {
    init?(serializedObject: Any?, request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?)
}

Codable

使用 Swift4 Codable 协议解析模型非常简单,只需实现 Codable 协议即可。

struct User: Codable {
  let name : String
  let id: Int
}

然后发送请求。

let request: APIRequest<User,APIError> = tron.codable.request("me")
request.perform(withSuccess: { user in
  print("Received user: \(user.name) with id: \(user.id)")
})

您可以为模型和错误解析自定义解码器。

let userDecoder = JSONDecoder()

let request : APIRequest<User,APIError> = tron.codable(modelDecoder: userDecoder).request("me")

JSONDecodable

TRON 提供了 JSONDecodable 协议,允许我们使用 SwiftyJSON 解析模型。

public protocol JSONDecodable {
    init(json: JSON) throws
}

要使用 SwiftyJSON 解析服务器的响应,只需创建一个符合 JSONDecodable 协议的类型,例如:

class User: JSONDecodable {
  let name : String
  let id: Int

  required init(json: JSON) {
    name = json["name"].stringValue
    id = json["id"].intValue
  }
}

然后发送请求。

let request: APIRequest<User,MyAppError> = tron.swiftyJSON.request("me")
request.perform(withSuccess: { user in
  print("Received user: \(user.name) with id: \(user.id)")
})

还包括了对于 Swift 内置类型(如 String、Int、Float、Double 和 Bool)的 JSONDecodable 协议的默认实现,因此你可以轻松地这样做:

let request : APIRequest<String,APIError> = tron.swiftyJSON.request("status")
request.perform(withSuccess: { status in
    print("Server status: \(status)") //
})

在不关心实际响应的情况下,你也可以使用 Alamofire.Empty 结构体。

包括数组响应序列化器在内的响应序列化的一些概念,在 容器类型解析文档 中有描述。

你可以自定义 JSONSerialization.ReadingOptions,这些选项用于 SwiftyJSON.JSON 对象在解析响应数据时。

let request : APIRequest<String, APIError> = tron.swiftyJSON(readingOptions: .allowFragments).request("status")

Swift 同步任务

使用 Swift 同步任务发送请求是通过代理对象 RequestSender(或者 DownloadRequestSender,用于下载请求)完成的。简单示例用法如下:

let request : APIRequest<User, APIError> = tron.codable.request("/me")
do {
 let user = try await request.sender().value
  // user variable contains User type
} catch {
  // Network request failed
}

如果你更喜欢接受包含成功模型或 ErrorModel 的结果的响应,也可以这样做。

let request : APIRequest<User, APIError> = tron.codable.request("/me")
let result = await request.sender().result
// result is Result<User,APIError>

还有一个名为 response 的异步属性,包含所有请求信息,如果需要。

let request : APIRequest<User, APIError> = tron.codable.request("/me")
let response = await request.sender().response
// response: AFDataResponse<Model>

上传请求

对于上传请求,监控上传进度并向用户展示非常有用。

let request : APIRequest<User, APIError> = tron.codable.request("/me/profile_picture")
  .upload("/post", fromFileAt: urlForResource("cat", withExtension: "jpg"))
  .method(.post)  

let sender = request.sender()
Task {
    for await progress in sender.uploadProgress {
      // Update progress view, progress: Progress
    }
}
let result = await sender.result

下载请求

同样地,下载请求也有实现了异步序列的 downloadProgress 属性。

Task {
    for await progress in sender.downloadProgress {
      // Update download view, progress: Progress
    }
}

如果你只关心下载文件 URL 而不是解析数据模型,你可以从请求发送器等待 responseURL 属性。

let destination = Alamofire.DownloadRequest.suggestedDownloadDestination()
let request: DownloadAPIRequest<URL, APIError> = tron
            .download("/download",
                      to: destination,
                      responseSerializer: FileURLPassthroughResponseSerializer())
do {
  let fileURL = try await request.sender().responseURL 
} catch {
  // Handle error
}

RxSwift

let request : APIRequest<Foo, APIError> = tron.codable.request("foo")
_ = request.rxResult().subscribe(onNext: { result in
    print(result)
})
let multipartRequest : UploadAPIRequest<Foo,APIError> = tron.codable.uploadMultipart("foo", formData: { _ in })
multipartRequest.rxResult().subscribe(onNext: { result in
    print(result)
})

错误处理

TRON 包含了内建的错误解析。 APIErrorErrorSerializable 协议的实现,它包含了一些有用的属性,可以从失败的请求中获取。

request.perform(withSuccess: { response in }, failure: { error in
    print(error.request) // Original URLRequest
    print(error.response) // HTTPURLResponse
    print(error.data) // Data of response
    print(error.fileURL) // Downloaded file url, if this was a download request
    print(error.error) // Error from Foundation Loading system
    print(error.serializedObject) // Object that was serialized from network response
  })

CRUD

struct Users
{
    static let tron = TRON(baseURL: "https://api.myapp.com")

    static func create() -> APIRequest<User,APIError> {
      tron.codable.request("users").post()
    }

    static func read(id: Int) -> APIRequest<User, APIError> {
        tron.codable.request("users/\(id)")
    }

    static func update(id: Int, parameters: [String:Any]) -> APIRequest<User, APIError> {
      tron.codable.request("users/\(id)").put().parameters(parameters)
    }

    static func delete(id: Int) -> APIRequest<User,APIError> {
      tron.codable.request("users/\(id)").delete()
    }
}

使用这些请求非常简单

Users.read(56).perform(withSuccess: { user in
  print("received user id 56 with name: \(user.name)")
})

也可以为您的 API 引入命名空间

enum API {}
extension API {
  enum Users {
    // ...
  }
}

这样您就可以像这样调用您的 API 方法

API.Users.delete(56).perform(withSuccess: { user in
  print("user \(user) deleted")
})

模拟请求

模拟请求直接内置于 APIRequest 中。所有您需要做的就是要设置 API 的模拟响应属性并将模拟请求功能打开

API.Users.get(56)
         .stub(with: APIStub(data: User.fixture().asData))
         .perform(withSuccess: { stubbedUser in
           print("received stubbed User model: \(stubbedUser)")
})

对于失败的请求的模拟也同样简单

API.Users.get(56)
         .stub(with: APIStub(error: CustomError()))
         .perform(withSuccess: { _ in },
                  failure: { error in
  print("received stubbed api error")
})

您还可以选择延迟模拟请求的响应时间

request.apiStub.stubDelay = 1.5

上传

  • 从文件
let request = tron.codable.upload("photo", fromFileAt: fileUrl)
  • 数据
let request = tron.codable.upload("photo", data: data)
let request = tron.codable.upload("photo", fromStream: stream)
  • 多部分表单数据
let request: UploadAPIRequest<EmptyResponse,MyAppError> = tron.codable.uploadMultipart("form") { formData in
    formData.append(data, withName: "cat", mimeType: "image/jpeg")
}
request.perform(withSuccess: { result in
    print("form sent successfully")
})

下载

let responseSerializer = TRONDownloadResponseSerializer { _,_, url,_ in url }
let request: DownloadAPIRequest<URL?, APIError> = tron.download("file",
                                                                to: destination,
                                                                responseSerializer: responseSerializer)

插件

TRON 包含了插件系统,可以响应大部分请求事件。

插件可以全局使用,在代码TRON实例本身使用,或本地使用,在具体的APIRequest上使用。请注意,添加到TRON实例的插件将在每个请求时被调用。全局和本地插件有一些非常有趣的应用场景。

默认情况下,不使用插件,但有两个插件作为TRON框架的一部分实现。

NetworkActivityPlugin

NetworkActivityPlugin用于监控请求并控制iPhone状态栏的网络活动指示器。此插件假定您在应用程序中只有一个TRON实例。

let tron = TRON(baseURL: "https://api.myapp.com", plugins: [NetworkActivityPlugin()])

NetworkLoggerPlugin

NetworkLoggerPlugin用于以可读的格式将响应记录到控制台。默认情况下,它只打印失败的请求,跳过成功的请求。

本地插件

对于本地插件有一些非常有趣的概念,其中一些在专门的PluginConcepts页面中进行了描述。

替代品

我们致力于构建最好的与RESTful Web服务交互的工具。然而,我们知道每个工具有其用途,因此了解其他哪些工具可以实现相同的目标始终很有用。

TRON受到Moya框架和LevelUPSDK的极大启发,后者已不再作为开源项目存在。

许可证

TRON 采用 MIT 许可协议发布。有关详细信息,请参阅 LICENSE 文件。

关于 MLSDev

MLSDev.com

TRONMLSDev, Inc. 维护。我们专注于提供移动和网站开发的全套解决方案。我们的团队遵循精益原则,采用敏捷方法工作,以提供最佳结果,降低开发成本和项目时间表。

更多详情请访问 这里,并请随时 联系我们