Stapler 0.1.0

Stapler 0.1.0

Nikita Belousov维护。



Stapler 0.1.0

  • Nikita Belousov

Stapler


Stapler 是一个用于 iOS 的 Swift 微框架,它可以 封装获取和刷新分页数据的所有逻辑Stapler 执行必要的后端请求,并提供一个可以用于 UI 绑定的准备好了的反应式数据源。

不再需要重新发明轮子 – Stapler 将在下一次您需要在集合视图中显示分页数据时为您节省大量时间。

请注意,Stapler 严重依赖于 ReactiveSwift。您可能还希望使用 ReactiveCocoa 以便轻松进行 UI 绑定。您应该熟悉这两个框架,尽管您可以使用自己的反应式扩展来替换(或与)ReactiveCocoa。

Carthage compatible CocoaPods compatible GitHub release Swift 4.0 platforms

安装

Carthage

如果您使用 Carthage 来管理您的依赖项,只需将 Stapler 添加到您的 Cartfile

github "neekeetab/Stapler" ~> 0.1

如果您使用 Carthage 来构建您的依赖项,请确保您已经将 Stapler.frameworkReactiveSwift.frameworkReactiveCocoa.frameworkResult.framework 添加到目标中 "Linked Frameworks and Libraries" 部分,并且在 Carthage 框架复制构建阶段中包含它们。

CocoaPods

如果您使用 CocoaPods 来管理您的依赖项,只需将 Stapler 添加到您的 Podfile

pod 'Stapler', '~> 0.1'

Git 子模块

  1. Stapler 仓库添加为您的应用程序仓库的子模块。
  2. Stapler 文件夹内部运行 git submodule update --init --recursive
  3. Stapler.xcodeprojCarthage/Checkouts/Result/Result.xcodeprojCarthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeprojCarthage/Checkouts/ReactiveCocoa/ReactiveCocoa.xcodeproj 拖放到您的应用程序的 Xcode 项目或工作区中。
  4. 在您的应用程序目标设置中的“通用”标签页上,将 Stapler.frameworkReactiveSwift.frameworkReactiveCocoa.frameworkResult.framework 添加到“嵌入的二进制文件”部分。
  5. 如果您的应用程序目标完全不包含 Swift 代码,您还应该将 EMBEDDED_CONTENT_CONTAINS_SWIFT 构建设置设置为“是”。

使用方法

Stapler 使得分页处理变得简单。以下是使用它的方法:

1. PaginatedResponse 协议

使您的分页服务器响应与 PaginatedResponse 协议相匹配。

/**
 Protocol to conform data structures representing paginated server responses to.
 */
public protocol PaginatedResponse {
    associatedtype Item
    var items: [Item] { get }
    var total: UInt { get }
}

2. 视图模型

在视图模型中(或在不遵循 MVVM 的其他地方),初始化 Stapler,提供一个表示网络请求的 SignalProducer

/* Suppose we have this declared somewhere in the project
class NetworkService {

	//...

	struct ItemsResponse: PaginatedResponse {
		let total: UInt
		let items: Item
	}
	
	/// Sends ItemsResponse and completes on success, Error otherwise. 
	static func items(offset: UInt, size: UInt) -> SignalProducer<ItemsResponse, Error>
	
}
*/

class ViewModel {

	let stapler = Stapler(pageSize: 10) { offset, size in
		// return network request with PaginatedResponse as a value type
		NetworkService.items(offset: offset, size: size)
	}

}

注意!对于一个视图模型,这只需要 3 行代码!👌

3. 视图

现在剩下的就是将视图模型中的stapler绑定到你的视图上,这通常包含一个UICollectionView子类。你可以在示例项目部分找到一个如何操作的方法。

公开接口

Stapler聚合了将你的UI进行绑定的所有必需组件。以下是你可以获得的内容

/**
 Contains all the logic for fetching and refreshing paginated data. Provides
 reactive data source to bind your UI to. Standardizes the process of getting
 paginated content and (arguably) makes your life (a little bit) easier.
 */
open class Stapler<Response: PaginatedResponse, ResponseError: Error> {
    
    /**
     An action to perform on initial load (usually in viewDidLoad). Is
     convenient in case you need yet another loading indicator for the initial
     loading. If you don't need that, it's safe to start initial load with the
     refresh action below.
     */
    public let initialLoadAction: Action<(), (), ResponseError>
    
    /**
     An action to perform to reload the content. Loads first page only. To load
     the rest call loadNextPageIfNeeded().
     */
    public let refreshAction: Action<(), (), ResponseError>
    
    /**
     Property containing deserialized elements obtained from paginated server
     responses.
    */
    public let items: MutableProperty<[Response.Item]>
    
    /**
     Property containing the number of pages loaded. Initially is 0.
    */
    public let pages: MutableProperty<UInt>
    
    /**
     Property containing the total number of elements on the backend size.
     Initially is 0.
     */
    public let total: MutableProperty<UInt>
    
    /**
     Property that tells you wheather you should show an activity indicator for
     pages being loaded after the first page is loaded. Initially is false.
     Becomes true at the start of 2nd page loading and stays true until all data
     is loaded (no pages left). Note, that you can obtain execution status for
     the first page with startInitialLoadingAction.isRefreshing if it's an
     initial load or refreshAction.isRefreshing if it's a load after
     refreshing.
     */
    public let shouldShowNextPageActivityIndicator: Property<Bool>
    
    /**
     Signal that notifies about errors that appeared
     while loading 2nd page and further. Note, that you can access errors for
     the first page with startInitialLoadingAction.errors if it's an initial
     load or refreshAction.errors if it's a load after refreshing.
     */
    public let errors2ndPageAndLater: Signal<ResponseError, NoError>
    
    /**
     Loads next page if needed. Usually you should call this function when the
     last cell in a collection view becomes visible. It's safe to call
     loadNextPageIfNeeded() many times subsequently – if there's already a page
     being loaded, all of these calls will be ignored. If there are no pages left,
     calling this function will have no effect.
     */
    public func loadNextPageIfNeeded() 
    
    /**
     - parameters:
         - pageSize: Number of elements per page.
         - request: A closure to provide request to your backend for given offset
            and size.
     */
    public init(pageSize: UInt,
         request: @escaping (_ offset: UInt, _ size: UInt) -> SignalProducer<Response, ResponseError>) 
    
}

示例项目


为了更好地理解Stapler的工作方式,请查看示例项目。以下是运行它的步骤

  1. 克隆Stapler仓库。
  2. 从以下终端命令中,在Stapler项目根目录使用其中一个获取项目依赖
    • git submodule update --init --recursive ,如果你已经安装了Carthage
    • carthage checkout
  3. 打开Stapler.xcworkspace
  4. 构建Result-iOS方案
  5. 构建ReactiveSwift-iOS方案
  6. 构建ReactiveCocoa-iOS方案
  7. 构建Stapler方案
  8. 运行StaplerDemo目标

限制

在其当前实现中,Stapler仅支持基于偏移量的分页。因此,你的后端有两条要求

  • 请求必须将偏移量页大小作为参数
  • 每个响应必须包含总元素数量

有什么缺失的?

请随意请求它!

有问题吗?

请随意创建一个github问题!