katsana-sdk-ios 0.9.0

katsana-sdk-ios 0.9.0

测试已测试
语言语言 Obj-CObjective C
许可 自定义
发布上次发布2017年2月
SwiftSwift 版本3.0

Lutfi 维护。



 
依赖项
Siesta>= 0
XCGLogger>= 0
 

  • 作者
  • Wan Ahmad Lutfi

Siesta

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

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

  • OS: iOS 8+, macOS 10.11+
  • 语言: 使用 Swift 编写,支持 Swift 和 Objective-C 应用程序
  • 工具要求: Xcode 8,Swift 3  (请参阅 swift-2.x 分支以获得旧版支持)
  • 许可: MIT

概述

文档

  • 用户指南 — 深入!激动人心!充满示例!
  • API 文档 — 震惊!隐藏的宝藏!更多示例!
  • 规范 — 听起来并不迷人,但意外地 informative。

这是为什么?

问题

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

当响应数据到达时,您需要显示响应数据。除非请求的屏幕不再可见。除非其他当前可见的 UI 碎片偶然需要相同的数据。或即将需要。

您应该显示一个加载指示器(但留意可能导致无限旋转的竞争条件),显示用户友好的错误(但不要多余 —— 没有模态警报狗堆!),为用户提供重试机制……并在随后的请求成功时隐藏所有这些。

务必避免重复请求 —— 并且避免重复响应反序列化。反序列化应该在后台线程上进行,当然。哦,并且记住不要意外地在回调闭包中保留 ViewController / 模型 / 辅助器等东西。除非您应该这么做。

自然地,您会希望为每个创建的项目从头开始以稍微不同的方式重写所有这些。

可能发生什么错误?

解决方案

Siesta通过提供一种针对熟悉的以请求为中心的方案的资源为中心的替代方案,结束了这个头疼的问题。

Siesta提供了一个应用范围内的RESTful资源状态的观察模型。此模型回答了三个基本问题

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

……,并在这些问题的答案改变时广播通知。

Siesta处理所有转换和边缘情况,以包装并交付这些答案,让您专注于您的逻辑和UI。

功能

  • 将视图、模型和控制器的生命周期与网络请求生命周期解耦
  • 将请求启动与请求配置解耦
  • 消除易错的状态跟踪逻辑
  • 消除冗余的网络请求
  • 统一处理所有错误:编码、网络、服务器端和解析
  • 高度可扩展的多线程响应反序列化
  • 透明内置解析(您可以将其关闭)适用于JSON、文本和图像
  • 平滑的进度报告,考虑上传、下载和延迟)
  • 透明处理Etag / If-Modified-Since
  • 预烘焙的UI助手用于加载与错误处理、远程图像
  • 易于调试、可定制的日志记录
  • 用Swift编写,具有优秀的以Swift为中心的API,但…
  • ……但也非常适合Objective-C,这得益于兼容层。
  • 轻量级。不会变得有意识并试图摧毁您。
  • 鲁棒的回归测试
  • 文档更多文档

它不做的事情

  • 不会重新发明网络。Siesta将网络操作委派给您的首选库(默认情况下为URLSession,或Alamofire,或注入您自己的custom adapter)。
  • 不会隐藏HTTP。相反,Siesta努力展示HTTP的全部丰富性,同时提供简化常规使用模式的便利。您可以设计一个抽象层来满足自己的特定需求,或直接使用Siesta的很好的请求和响应实体的API。
  • 不会自动执行响应与模型映射。这意味着Siesta不会限制您的响应模型,或强迫您有任何模型。添加响应转换器以输出您偏好的任何风味模型,或直接与解析的JSON一起工作。

起源

该项目始于我们在几个Bust Out Solutions项目中的实际需求下编写的辅助代码。当我们发现自己需要在项目之间复制代码时,我们知道是时候开源了。

对于开源过渡,我们花了时间用Swift重写我们的代码——并以Swift的思维方式重写它,拥抱这门语言以使API尽可能简洁。

因此,Siesta的代码既是旧的也是新的:在App Store上经过战斗考验,然后在一个Swift风格的空白场地上重获新生。

设计哲学

让默认的事情在大多数时候是正确的。

让正确的事情始终简单。

从需求出发。不要为了解决问题而发明解决方案。

设计API时以以下目标为导向

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

……按照这个优先级顺序。


安装

Siesta需要Swift 3,因此请确保您安装了Xcode 8。(如果您还没有完成大型迁移,请使用swift-2.x分支。)

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支持的UI

您的袜子还在吗?

看看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版本具有更健壮的缓存行为,并且如果它被刷新的话,会自动更新所有显示的图像。)

在Siesta中已经包含了一个更加功能丰富的RemoteImageView版本(可在此找到Siesta的远程视图图像API),但这并不是重点。"代码更少"也并不是重点。重点是Siesta提供了一种优雅的抽象,它能够解决你真正遇到的问题,使你的代码更简单、更不易出错

与其他框架的比较

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

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

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

Siesta具有强大的功能,但并不试图解决所有问题。特别是Moya和RestKit解决了互补/替代问题,而Alamofire和AFNetworking提供了更强大的底层HTTP支持。进一步复杂化比较的是,一些框架建立在其他框架之上。例如,当你使用Moya时,你也在注册Alamofire。

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

Siesta Alamofire RestKit Moya AFNetworking URLSession
HTTP请求
异步响应回调
可观察的内存缓存
防止冗余请求
防止冗余解析
常见格式的解析
基于路由的解析
基于内容类型的解析
文件上传/下载任务 ~
对象模型映射
Core Data集成
隐藏HTTP
UI助手
主要语言 Swift Swift Obj-C Swift Obj-C Obj-C
非简单代码行² 2424 2455 13276 831 4025 ?
基于 任何(可注入) URLSession AFNetworking Alamofire NSURLSession / NSURLConnection Apple核心

1. 声明:表格由Siesta的非全能作者编译。需要纠正/添加?请提交PR
2. “简单”意味着只包含空白、注释、括号、分号和大括号的行。

尽管有这样一套功能列表,但Siesta是一个非常精简的代码库——与Alamofire相当重量级,而RestKit则轻85倍。

Siesta与其他框架的区别

不仅仅是因为特性。Siesta解决的是与其他REST框架不同的问题

其他框架基本上把HTTP看作是一种远程过程调用的形式。新信息只在请求对应的响应中到来——异步函数的返回值。

Siesta在“REST”中“重新加入”了“ST”,即在架构原则中拥抱了“状态转移”的概念,将观察状态的行为与转移状态的行为分离。

如果这种做法听起来吸引人,那么就试用一下Siesta吧。


文档

示例

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

  1. 如果您尚未安装,请安装 CocoaPods ≥ 1.0。
  2. pod try Siesta (注意不需要先本地下载/克隆 Siesta;此命令会为您做到这一点。)

贡献和寻求帮助

要报告错误,请提交问题

要指出文档中的错误或混淆之处,请提交问题

要提交功能请求/好主意,请提交问题

要寻求帮助,请将您的问题发布到Stack Overflow,并使用标签 siesta-swift。 (请务必包括标签。它会触发通知。)

拉取请求

想要点事情,而不仅仅是讨论?太棒了!大胆行动。

  • 如果您正在提出设计更改或非平凡的新功能,请先将您的想法作为一个问题提出,这样您就不会白费力气。
  • 请遵循现有代码的格式约定。是的,包括 Paul 在括号位置上的古怪品味。(匹配的括号总是在同一行或同一列,并且始终与它们所包含的代码对齐。)
  • 请确保在本地测试通过(Xcode 中的 cmd-U)。
  • 在您的拉取请求被接受之前,可能会有一些来回讨论。不要气馁。吹毛求疵不意味着工作做得不好;这是对项目感兴趣的表现!
  • 如果您在构建或测试项目时遇到问题,请提交一个关于该问题的问题描述拉取请求——即使您已经解决了这个问题。这将有助于改进文档。
  • 如果您想更新用户手册,请注意,手册是从 master 分支中的 Docs 目录生成的,因此您应该在您修改位置进行更改。不要针对 gh-pages 提交拉取请求。