Siesta 1.5.2

Siesta 1.5.2

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

Paul Cantrell维护。



Siesta 1.5.2

Siesta

编写iOS/macOS REST客户端的优雅方式

Build Status Version Platforms Docs GitHub license Twitter: @siestaframework

通过提供RESTful资源的可观察模型客户端缓存来极大地简化应用程序代码。

  • 操作系统 iOS 10+、macOS 10.11+、tvOS 9.0+
  • 语言 用Swift编写,支持Swift和Objective-C应用程序
  • 工具要求 Xcode 11.3+、Swift 5.1+(有关对旧版的支撑,请见swift-* 分支
  • 许可证 MIT

目录

概览

文档

  • 用户指南 — 丰富!激动!满是示例!
  • API文档 — 完美无缺的细节!隐藏的宝藏!更多示例!
  • 规范 —— 听起来好像不起眼,但却提供了惊人的有用信息。

这是用它做什么的?

问题所在

想要您的应用与远程API通信?欢迎来到您的噩梦状态!

每当响应数据到达时,您需要显示该数据。除非请求屏幕不再可见。除非某个当前可见的UI元素恰好需要相同的数据。或者,它即将需要。

应该显示加载指示器(但要注意可能导致它永远不断旋转的竞争条件),显示对用户友好的错误信息(但不能重复——不要弹出模态警告!),提供用户重试机制……当后续请求成功时,隐藏所有这些操作。

确保避免冗余请求——以及冗余的响应反序列化。当然,反序列化应该在后台线程上进行。记得,不要在没有意识的情况下在回调闭包中保留您的ViewController/模型/助手对象。除非您应该这么做。

自然地,您会想要在创建每一个新项目时,以略有不同的即兴方法从头开始重写所有这些。

可能出什么问题呢?

解决方案

Siesta通过提供针对熟悉的以请求为中心的方法的《以资源为中心》的替代方案来结束这个头痛。

Siesta提供了一个应用级别的可观察模型,以RESTful资源的状态作为模型。此模型回答三个基本问题

  • 此资源最新的数据是什么,如果有的话?
  • 最新的请求是否产生了错误?
  • 是否有请求正在进行中?

……并且当这些问题的答案发生变化时,它会发送通知。

Siesta处理所有转换和边缘案例,以封装这些答案,并精美地包装上,让您能够专注于您的逻辑和UI。

特性

  • 解耦视图、模型、控制器生命周期与网络请求生命周期
  • 解耦请求发起与请求配置
  • 消除易出错的状态跟踪逻辑
  • 消除冗余的网络请求
  • 对所有错误提供统一处理:编码、网络、服务器端、解析
  • 高度可扩展,多线程响应反序列化
  • 对 JSON、文本和图像提供透明内置解析(可关闭)
  • 考虑上传、下载以及延迟的平滑进度报告
  • 透明处理 Etag / If-Modified-Since
  • 预包装的 UI 辅助器,用于加载和错误处理、远程图片
  • 易于调试的自定义日志功能
  • 使用 Swift 编写,采用优秀的以 Swift 为中心的 API(Siesta API),但是...
  • ...也通过兼容层,在 Objective-C 中运行良好。
  • 轻量级。不会获得智慧并尝试摧毁你。
  • 稳健的回归测试
  • 文档更多文档

它不能做什么

  • 不会重新发明网络。Siesta 将网络操作委托给您选择的库(默认为 URLSession,或 Alamofire,或注入您自己的自定义适配器)。
  • 不隐藏 HTTP。相反,Siesta 力求暴露 HTTP 的全部丰富性,同时提供便利,简化常见的使用模式。您可以构建一个适用于自己特定需求的抽象层,或直接使用 Siesta 的优秀 API 来处理请求和响应实体。
  • 不会自动进行响应与模型的映射。这意味着 Siesta 不会限制您的响应模型,或强迫您有任何模型。您可以添加响应转换器以输出应用程序偏好的模型,或者直接与解析后的 JSON 一起工作。

起源

这个项目最初是我们为几个Bust Out Solutions项目编写的辅助代码,出于实际需要。当我们发现自己在项目之间复制代码时,我们知道是时候开源它了。

为了开源迁移,我们花费了时间将我们的代码重写为 Swift,并且在 Swift 中重新思考它,拥抱这种语言使得 API 如概念一样干净。

因此,Siesta 的代码既是旧的也是新的:在应用商店经过实战检验后,又在 Swifty 的新绿地上重生。

设计哲学

让默认之事成为大多数时候的正确之事。

让正确之事始终变得容易。

从需求出发。不要寻找问题去发明解决方案。

设计API遵循以下目标

  1. 使客户端代码易于阅读
  2. 使客户端代码易于编写
  3. 保持API整洁。
  4. 保持实现有序。

……按此优先级顺序。


安装

Siesta需要Swift 5和Xcode 11。(如果您仍在使用较旧版本,请使用swift-*分支。)

Swift Package Manager

在Xcode中

  • 文件 → Swift 包 → 添加包依赖…

  • 在URL字段中输入https://github.com/bustoutsolutions/siesta并点击下一步。

  • 版本设置的默认值适合大多数项目。点击下一步。

  • 勾选“Siesta”旁边的复选框。

    • 如果您想使用任何UI辅助工具,请也勾选“SiestaUI”。
    • 如果您想使用Siesta的Alamofire扩展,请也勾选“Siesta_Alamofire”。
  • 点击“完成”。

  • Siesta尚不支持由依赖提供的资源。这意味着,如果您

    • 在上面包含了SiestaUI
    • 并且您计划使用ResourceStatusOverlay
    • 并使用其默认初始化器而不是提供自己的自定义UI布局,

    ……那么您需要将ResourceStatusOverlay.xib复制到您自己的项目中。

    SwiftPM最近增加了对该特性的支持,Siesta将在下一个版本中添加它。

请注意,Xcode将显示Siesta的所有可选和测试只依赖项,包括Quick、Nimble和Alamofire。请放心:这些实际上不会被打包到您的应用中(除非您使用Alamofire)。

CocoaPods

在您的Podfile

pod 'Siesta', '~> 1.0'

如果您想使用UI辅助工具,请按以下操作:

pod 'Siesta/UI', '~> 1.0'

如果您想使用 Alamofire 作为您的网络提供程序,而不是使用 Foundation 的 URLSession

pod 'Siesta/Alamofire', '~> 1.0'

(您还需要在配置您的 Siesta.Service 时传递一个 Alamofire.Manager。有关更多信息,请参阅 API 文档。)

Carthage

在您的 Cartfile

github "bustoutsolutions/siesta" ~> 1.0

按照 Carthage 指令Siesta.framework 添加到您的项目中。如果您想使用 UI 辅助工具,您还需要将 SiestaUI.framework 添加到您的项目中。

截至本文撰写之时,您需要遵循一个附加步骤,Carthage 文档中并未提及

  • 构建设置 → 框架搜索路径 → $(PROJECT_DIR)/Carthage/Build/iOS/

(有关 Xcode 近期版本的 Carthage 的深入讨论见 此处。)

Extensions/ 中的代码并不属于 Carthage 构建出的 Siesta.framework。 (这目前仅包括 Alamofire 支持。)如果您想使用这些源文件,需要手动将它们包含到您的项目中。

Git Submodule

  1. 将 Siesta 作为子模块克隆到您选择的目录中,在本例中为 Libraries/Siesta

    git submodule add https://github.com/bustoutsolutions/siesta.git Libraries/Siesta
    git submodule update --init
    
  2. Siesta.xcodeproj 作为子项目拖入您的项目树中。

  3. 在您的项目构建阶段下,展开目标依赖。点击加号按钮,并添加 Siesta。

  4. 展开链接二进制库阶段。点击加号按钮,并添加 Siesta。

  5. 在右上角点击加号按钮添加一个复制文件构建阶段。设置目录为 Frameworks。点击加号按钮,并添加 Siesta。

如果您想使用 UI 辅助工具,需要重复步骤 3-5,针对 SiestaUI 进行操作。

安装难题?

告诉我们,即使您最终找到了解决方案。了解人们在何处遇到难题将有助于改进这些说明!


基本用法

为要使用的 REST API 创建一个共享服务实例

let MyAPI = Service(baseURL: "https://api.example.com")

现在注册您的视图控制器——或者视图,内部粘合类,反应性信号/序列,您喜欢的东西——以便在特定资源状态改变时接收通知

override func viewDidLoad() {
    super.viewDidLoad()

    MyAPI.resource("/profile").addObserver(self)
}

使用这些通知来填充您的UI

func resourceChanged(_ resource: Resource, event: ResourceEvent) {
    nameLabel.text = resource.jsonDict["name"] as? String
    colorLabel.text = resource.jsonDict["favoriteColor"] as? String

    errorLabel.text = resource.latestError?.userMessage
}

或者如果您不喜欢委托,Siesta支持闭包观察者

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, _ in

    self?.nameLabel.text = resource.jsonDict["name"] as? String
    self?.colorLabel.text = resource.jsonDict["favoriteColor"] as? String

    self?.errorLabel.text = resource.latestError?.userMessage
}

注意,当我们调用jsonDict时,并不会进行实际的JSON解析。JSON已经在主线程外、在GCD队列上被解析,而且与其他框架不同,它只解析一次,不管有多少观察者。

当然,您可能不希望在所有控制器中都使用原始JSON。您可以配置Siesta自动将原始响应转换为模型

MyAPI.configureTransformer("/profile") {  // Path supports wildcards
    UserProfile(json: $0.content)         // Create models however you like
}

……现在观察者看到的是模型而不是JSON了

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, _ in
    self?.showProfile(resource.typedContent())  // Response now contains UserProfile instead of JSON
}

func showProfile(profile: UserProfile?) {
    ...
}

在视图出现时触发一个感知陈旧性、抑制重复请求的加载

override func viewWillAppear(_ animated: Bool) {
    MyAPI.resource("/profile").loadIfNeeded()
}

……这样您就有了网络化的UI。

添加一个加载指示器

MyAPI.resource("/profile").addObserver(owner: self) {
    [weak self] resource, event in

    self?.activityIndicator.isHidden = !resource.isLoading
}

……或者最好使用Siesta的预配置ResourceStatusOverlay视图,免费获得活动指示器、格式良好的错误信息以及重试按钮

class ProfileViewController: UIViewController, ResourceObserver {
    @IBOutlet weak var nameLabel, colorLabel: UILabel!

    @IBOutlet weak var statusOverlay: ResourceStatusOverlay!

    override func viewDidLoad() {
        super.viewDidLoad()

        MyAPI.resource("/profile")
            .addObserver(self)
            .addObserver(statusOverlay)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        MyAPI.resource("/profile").loadIfNeeded()
    }

    func resourceChanged(_ resource: Resource, event: ResourceEvent) {
        nameLabel.text  = resource.jsonDict["name"] as? String
        colorLabel.text = resource.jsonDict["favoriteColor"] as? String
    }
}

注意,这个例子不是玩具代码。与其故事板一起,这个小的类是一个全副武装且可以正常运作的基于REST的用户界面

袜子还在吗?

看看AFNetworking对UIImageView的受人尊敬的扩展,用于异步按需加载数据和网络图像。真的,去快速查看这段代码并理解所有它所做的酷炫事情。花几分钟时间。我会等你的。我是一个README。我不会走开。

明白了?好。

这是您如何使用Siesta实现相同功能的示例

class RemoteImageView: UIImageView {
  static var imageCache: Service = Service()
  
  var placeholderImage: UIImage?
  
  var imageURL: URL? {
    get { return imageResource?.url }
    set { imageResource = RemoteImageView.imageCache.resource(absoluteURL: newValue) }
  }
  
  var imageResource: Resource? {
    willSet {
      imageResource?.removeObservers(ownedBy: self)
      imageResource?.cancelLoadIfUnobserved(afterDelay: 0.05)
    }
    
    didSet {
      imageResource?.loadIfNeeded()
      imageResource?.addObserver(owner: self) { [weak self] _,_ in
        self?.image = self?.imageResource?.typedContent(
            ifNone: self?.placeholderImage)
      }
    }
  }
}

这两个版本的缩略图,供您比较代码使用

Code comparison

相同的函数

(好吧,好吧,它们并不完全相同。Siesta版本具有更健壮的缓存行为,并且如果刷新,它会自动更新显示图片。)

已经包括了一个更完善的RemoteImageView版本在Siesta中 —— 但UI免费资源不是重点。即使是“更少的代码”也不是重点。重点是Siesta提供了你真正需要的优雅的抽象 ,这可以解决你的问题,使你的代码更简单,更不易破碎

与其他框架的比较

流行的REST/网络框架有不同的主要目标

  • URLSession是苹果的标准iOS HTTP库(并且通常是大多数项目的需求)。
  • Siesta通过可观察资源缓存解决了状态问题。
  • Alamofire为URLSession提供了一个Swifty、现代感包装器。
  • Moya 通过封装 Alamofire 来隐藏 HTTP URL 和参数。
  • RestKit 将 HTTP 与 JSON 相结合对象模型Core Data 映射。
  • AFNetworking 是一个现代感的 Obj-C 网络API包装器,包括一系列相关实用工具。

哪个最适合你的项目?这取决于你的需求和口味。

Siesta 功能强大,但并不试图解决一切。特别是,Moya 和 RestKit 解决互补/替代问题,而 Alamofire 和 AFNetworking 提供更稳健的底层 HTTP 支持。进一步的比较变得更加复杂,一些框架建立在其他框架之上。例如,当你使用 Moya 时,你也在使用 Alamofire。Siesta 默认使用 URLSession,但如果你想使用其 SSL 信任管理功能,也可以建立在 AlibabaQuick 之上。组合的方式很多。

考虑到所有这些,以下是一些功能对比¹

Siesta Alamofire RestKit Moya AFNetworking URLSession
HTTP 请求
异步响应回调
内存缓存
防止重复请求
防止重复解析
解析常见格式
基于路由的解析
基于内容类型的解析
文件上传/下载任务 ~
对象模型映射
Core Data 集成
隐藏 HTTP
UI 辅助工具
主要语言 Swift Swift Obj-C Swift Obj-C Obj-C
非简单行代码² 2609 3980 13220 1178 3936 ?
建立在 任何(可注入的) URLSession AFNetworking Alamofire NSURLSession / NSURLConnection 苹果核心

1. 声明:此表由 Siesta 的非全能作者编制。纠正/补充?请 提交 PR
2. “简单”指只包含空白、注释、括号、分号和大括号的行。

尽管有此功能列表,Siesta 仍然是一个相对紧凑的代码库——比 AlibabaQuick 小,比 RestKit 轻了 5.5 倍。

Siesta 有何独特之处?

不仅仅是有功能。Siesta 解决了其他 REST 框架不同的一个问题

其他框架本质上将 HTTP 视为一种 远程过程调用 的形式。新信息只有在与请求结合的响应中才会出现——异步函数的返回值。

Siesta 恢复了 “REST” 中 “ST” 的含义,将 “状态转移” 视为一个架构原则,并将观察状态的行为与转移状态的行为解耦。

如果这种方法听起来吸引人,不妨试一下 Siesta。


文档

示例

此仓库包含一个简单的示例项目 示例项目链接。要下载示例项目,安装其依赖项,并在本地运行

  1. 如果您还没有安装,请安装 CocoaPods ≥ 1.0。
  2. pod try Siesta (注意:不需要先本地下载克隆 Siesta;此命令会为您完成)

支持

寻求帮助,请在 Stack Overflow 上发帖,并使用 siesta-swift 作为标签。 (请确保包含该标签。它会触发 Siesta 核心团队的提醒。) 这比提交问题更好,因为其他人可能也遇到同样的问题,而 Stack Overflow 的答案比已关闭的问题更易被发现。

Stack Overflow 上应解决的问题

  • “我该如何...?”
  • “有方法...吗?”
  • “Siesta 适合...吗?”
  • “我遇到了这个错误...”

对于 错误、功能请求或好主意,请在 Github 上提交问题。属于 Github 问题的内容

  • “当我执行 操作 x 时,我期望得到 结果 y 但得到的是 结果 z
  • “应该有一种方法...”
  • 项目 x 的文档缺失/表述不清”

不确定选择哪个?如果您是 提出对 Siesta 的更改,请使用 Github 的问题。如果您询问的是不改变项目的 问题,那么使用 Stack Overflow。

两件大小适中但很重要的事情

请注意,Siesta 是由志愿者维护的。如果您收到问题的答案需要一段时间,请耐心等待;我们都有工作、家庭、义务以及在此项目之外的生活。

请相互尊重,并遵守我们的行为守则