Gnomon 5.2.6

Gnomon 5.2.6

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2021 年 9 月
SPM支持 SPM

Vladimir BurdukovSergei MikhanEugene FilipkovDmitry DulebaEugene_Filipkov 维护。



Gnomon 5.2.6

  • Vladimir Burdukov

Gnomon

Build Status CocoaPods Compatible Swift Package Manager compatible

另一个网络库?

我们为什么决定开发一个新的网络库呢?

  1. HTTP 缓存以获得更好的用户体验。显示过时内容比显示无限加载指示符要好。为了实现这一点,Gnomon 可以执行双请求:首先请求接收缓存的(可选)内容,第二次 HTTP 调用。此外,即使没有互联网连接,库也会返回缓存版本。

  2. RxSwift 接口。我们决定,使用 Rx 可观察对象来处理此类复合响应要容易得多:第一个 .next 事件携带缓存响应,第二个 – 来自网页服务器的实际响应。

  3. 将模型作为请求数据的结果而不是数据。这是应用程序接收到数据后的常规步骤 - 将其解析到某个模型中。我们发现让库用户跳过这个阶段更好:每个请求都有一个通用的参数 - 响应模型类型。结果订阅者接收模型或模型的数组,而不是原始数据。

安装

pod 'Gnomon', '~> 3.0'

使用说明

定义模型

我们定义了一个灵活的通用协议 BaseModel 以及几个扩展 (JSONModel, XMLModel, DecodableModel)。

在大多数情况下,我们使用 JSONModel,它使用 SwiftyJSON 作为解析器,提供了一个轻量级的接口来解析模型属性。

import Gnomon
import SwiftyJSON

struct UserModel: JSONModel {

  let id: Int
  let login: String
  let avatarUrl: URL?
  let profileUrl: URL?

  init(_ json: JSON) throws {
    id = json["id"].intValue
    login = json["login"].stringValue
    avatarUrl = json["avatar_url"].url
    profileUrl = json["html_url"].url
  }

}

准备一个请求

我们有一个灵活的 RequestBuilder,它只需要 URL 字符串参数,但提供了一个构建器接口来构建复杂的 HTTP 请求。

库中有 4 种 Result 类型 – 它们配置了库如何解析您的响应

  • SingleResult<T> 单个模型 T,如果解析失败,请求将失败
  • MultipleResults<T> 模型数组 T,如果解析失败,请求将失败
  • SingleOptionalResult<T> 单个可选模型 T?,如果解析失败,请求返回 nil
  • MultipleOptionalResults<T> 可选模型数组 T?,如果解析失败,请求返回包含模型和 nil 的混合数组
func prepareRequest() throws -> Request<MultipleOptionalResults<UserModel>> {
  return try RequestBuilder()
    .setURLString("https://api.github.com/users").build()
}

此 API 调用返回字典数组,但我们经常遇到只解析 JSON 的特定部分的多层 JSON 返回调用。在这种情况下,您可以在构建器上添加 setXPath(),路径由 / 分隔(例如 "document/result/data")。

创建一个可观察对象并订阅它

如我们上面所述,Gnomon 接口基于 RxSwift – 当你想要进行请求时,你需要创建一个可观察的对象并然后订阅它。

import RxSwift

let disposeBag = DisposeBag()

func loadData() {
  do {
    let request = try prepareRequest()
    Gnomon.cachedThenFetch(request).subscribe(onNext: { response in
      print(response.result.models)
    }).disposed(by: disposeBag)
  } catch {
    print("can't prepare request: \(error)")
  }
}

第一次运行此代码时,我们将在 stdout 中看到两条日志:空数组和 UserModel 选项数组。第一次调用后,URLSession 将响应存储在共享的 URLCache 中,在下一次调用中,我们将收到两次相同的数组。

如果你收到作为第二次响应的缓存版本,你可以通过省略 UI 更新来优化你的 UI 更新逻辑。这是由于 URLSession 内部的逻辑:它自己检查缓存到期/有效性和返回两次缓存响应。你可以通过 response.responseType 属性检测 HTTP 缓存结果。

import RxSwift

let disposeBag = DisposeBag()

func loadData() {
  do {
    let request = try prepareRequest()
    Gnomon.cachedThenFetch(request).subscribe(onNext: { [weak self] response in
      guard response.responseType != .httpCache else { return }
      self?.updateUI(with: response.result.models)
    }).disposed(by: disposeBag)
  } catch {
    print("can't prepare request: \(error)")
  }
}

func updateUI(with models: [UserModel?]) {}

天文观测仪

日晷是天文观测仪最好的朋友:)

你可以使用天文观测仪和 Loaders 轻松地将你的模型转换为适用于 UITableView 或 UICollectionView 的单元格。

import Astrolabe
import Gnomon

class UsersViewController: UIViewController, Loader {
  let tableView = TableView<LoaderDecoratorSource<TableViewSource>>
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.source.loader = self
  }

  func load(for intent: LoaderIntent) -> SectionObservable? {
    return Astrolabe.load(pLoader: self, intent: intent)
  }
}

extension UsersViewController: PLoader {
  typealias PLResult = MultipleOptionalResults<UserModel>
  
  func request(for loadingIntent: LoaderIntent) throws -> Request<PLResult> {
    return try RequestBuilder()
      .setURLString("https://api.github.com/users").build()
  }
  
  func sections(from result: PLResult, loadingIntent: LoaderIntent) -> [Sectionable]? {
    let users = result.models.flatMap { $0 }
    return [Section(cells: users.map { TableCell<UserTableViewCell>(data: $0) })]
  }
}