WinkKit
一个 iOS 框架,包含一系列遵循 MVP 模式的类,并以 Swift 编写,用于解决一些常见问题,用于 Wink 应用。遵循此指南了解如何结构化 Wink iOS 项目。
目录
开始使用
先决条件
您需要安装 Xcode 9、Swift 4 和 CocoaPods。
将项目/文件模板添加到Xcode中(可选但推荐)
WinkKit已设计用于帮助使用MVP模式创建应用程序(您将在稍后更好地理解这一点);遵循此模式,需要为每个视图创建几个文件。WinkKit包含一组Xcode模板,以加快项目和文件创建的速度;下载Wink Project Helper macOS应用程序以安装所有模板。
在模板安装后,在Xcode中,在文件 > 新建 > 文件(或CMD+N)下,您可以创建符合MVP视图控制器、表格视图单元格和集合视图单元格,如下所示。
您甚至可以创建整个项目,请看下一点。
安装
使用模板创建项目
如果您是从头开始,并且成功安装了Wink Kit模板,您可以直接从Xcode中创建新项目,在文件 > 新建 > 项目
此模板为您创建了Wink应用程序的基本结构(稍后将解释),已配置好包含WinkKit框架的Podfile
,.gitignore
和其他模板文件/代码。
现在在根项目文件夹中运行pod install
,然后从工作区文件重新打开项目。这样就完成了。
手动
只需将CocoaPods依赖项粘贴到您的Podfile
中。由于cocoapods的错误,请确保也粘贴了post_install
函数。
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
# https://github.com/WINKgroup/WinkKit
pod 'WinkKit'
end
# This post install is needed because of a Cocoapods bug; it is needed to render WinkKit properties in InterfaceBuilder correctly.
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if target.name === 'AlamofireImage'
config.build_settings['SWIFT_VERSION'] = '3.3' # set swift 3.3 on AlamofireImage
end
config.build_settings['CONFIGURATION_BUILD_DIR'] = '$PODS_CONFIGURATION_BUILD_DIR'
end
end
end
注意:AlamofireImage只能在Swift 3.3中编译。
API 文档
这里。
在此检查类引用理解结构
在讨论框架的类之前,我们将先看看架构结构。
这是一个 MVP 模式;查看这个 iOS 架构概述 来理解 MVC、MVP、MVVM、VIPER 之间的差异。
Wink iOS 项目 应该 按以下方式进行结构化,特别是如果项目将有很大的增长
映射此架构的整个 Xcode proj 结构大致如下
表示层
这是包含所有 iOS 框架专用类的层,例如 UIKit
框架。我们可以说,这个层不能在没有 iPhone/iPad 的情况下存在,因为 UIKit
只能在那里运行。
- AppDelegate:这是众所周知的 AppDelegate 类,没有什么特别的;
- 用例:一个包含所有用例的组。重要的是要理解,一个 用例 是用户在应用屏幕上执行的一个或多个动作,例如登录。
- 登录:一个用例的示例。它将包含所有相关的 ViewControllers、Presenter(如果用例包含多个),DataSources 等。
- 登录展示者:一个简单的展示者;登录展示者保留登录视图控制器的状态;展示者是一个包含逻辑的类,视图控制器不包含逻辑。 展示者不包含 UIKit 类! 这是为了使展示者易于测试。
- 登录视图控制器:在经典 MVC 模式下,(iOS 世界的“大量视图控制器”
😫 所有逻辑都在这里,与视图处理混合在一起;在这个框架中,ViewController 拥有一个Presenter,并委派其逻辑。例如,ViewController没有func performLogin(email: String, password: String)
这样的方法;取而代之的是,Presenter有。ViewController只会接收用户输入,并通知Presenter发生了什么事情。Presenter会执行工作,并告诉ViewController视图应该改变。
- 登录:一个用例的示例。它将包含所有相关的 ViewControllers、Presenter(如果用例包含多个),DataSources 等。
- 核心:一个包含在项目中可重用基类的组。定义这些类是一个好习惯,可以避免代码重复,从而提高维护难度。
- 资源:所有资源都放在这里,包括.xcassets、自定义字体等……
数据
这是处理所有数据层级的层,例如http调用、到达后端的上传/下载缓存。在这个层中不使用任何UIKit
类!
- 缓存:一个包含SessionManager和其他所有本地保存数据的类的组。
- 网络:包含HttpClient的组,必须用Alamofire实现。WinkKit本身在框架中提供了Alamofire和Alamofire Image,因此您不需要在Podfile中添加任何东西。
- 响应序列化:包含Alamofire的
DataResponse
扩展:它提供对返回对象而不是json的http调用的公共响应;json解析在这个扩展中完成(详情请参阅源文件)。注意,这个扩展使用Argo进行json解析。 - 资源:一个将https调用响应映射到枚举。
- 错误:将http错误(客户端和服务器)映射到类/结构。
- 路由器:路由器负责了解api的端点,并为Services创建用于执行http调用的
urlRequest
。 - 服务:服务执行http调用,使用路由器创建的请求。
- 响应序列化:包含Alamofire的
表示
使用增强的视图
WinkKit提供了具有更多@IBDesignable
的通用视图类。
- WKView
- WKImageView
- WKButton
- WKLabel
- WKTextView
每个类都继承自 UIKit
,例如 WKView
继承自 UIView
。要在 InterfaceBuilder 中使用这些类,从对象库中拖拽对象,并让它继承期望的 WinkKit 视图。例如,要使用 WKButton
,从对象库中拖拽一个按钮,然后在“身份检查器”中设置自定义类。
确保将 WinkKit 保持为模块。
然后你可以从“属性检查器”中自定义按钮。
使用 ViewControllers 和 Presenters
在 WinkKit 应用中,每个视图控制器都应该继承自 WKViewController<P>
(或 WKTableViewController<P>
或 WKCollectionViewContrller<P>
,它们都有相同的行为)。
WKViewController
希望有一个 WKGenericViewControllerPresenter
的子类(这是一个扩展基本呈现器协议 WKPresenter
的协议),因为视图控制器生命周期绑定到这种呈现器。主页的一个典型实现是:
// HomeViewController.swift
class HomeViewController: WKViewController<HomePresenter> {
// do only UI stuff here
}
// Since the view controller is handled by HomePresenter, it must be conform to LoginView.
extension HomeViewController: LoginView {
// implements all HomeView methods/properties
}
// HomePresenter.swift
// Define what the view can do
protocol LoginView: PresentableView {
}
class HomePresenter: WKGenericViewControllerPresenter {
typealias View = LoginView // need to tell the protocol which is the view handled
weak var view: LoginView? // keep view weak to avoid retain-cycle since view controller holds a reference to this presenter
required init() {} // framework wants empty init
// do all logic here, such as use a Service to fetch data and tell the view to update
}
请注意,您不需要调用任何方法来绑定视图控制器和呈现器,所有这些操作都是框架自动完成的!
HomePresenter 和 HomeViewController 是两个不同的文件。您可以使用文件模板快速创建这种结构
使用 Table Views 和 Collection Views
WinkKit 提供了 WKTableView
、WKCollectionView
和 WKTableViewCell
、WKCollectionViewCell
。让我们来谈谈 WKTableView
和 WKTableViewCell
(集合视图有相同的逻辑)。
注意:为了有一个更好的结构,所有单元格都必须有一个 xib:请不要在Storyboard中直接创建单元格。
WKTableView
提供了两种方法来快速注册和排练一个 WKTableViewCell
,只需这样做:
tableView.register(cell: ItemTableViewCell.self) // register the cell with a xib that has same name of the class
tableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath) // dequeue a cell, already casted
单元格就像视图控制器一样:它们有一个呈现器(在这种情况下是简单的 WKPresenter
),并且必须符合呈现器处理的视图。因此,创建单元格就像创建视图控制器一样
// ItemTableViewCell.swift
class ItemTableViewCell: WKTableViewCell<ItemPresenter> {
// do only UI stuff here
}
extension ItemTableViewCell: ItemView {
// implements all ItemView methods/properties
}
// ItemPresenter.swift
/// The protocol that the cell handled by presenter must conforms to.
protocol ItemView: PresentableView {
}
/// The presenter that will handle all logic of the view.
class ItemPresenter: WKPresenter {
typealias View = ItemView
// The view associated to this presenter. Keep weak to avoid retain-cycle
weak var view: ItemView?
// the item that will be showed in this cell
private var item: Item!
init(with item: Item) {
self.item = item
}
// do all logic here
}
您可以使用模板快速创建所有这些类/协议
与视图控制器不同,单元格在数据源中排练后必须进行配置,如下所示
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath)
let presenter = ItemPresenter() // create a presenter
cell.configure(with: presenter) // configure the cell with the presenter
return cell
}
集合视图和表视图数据源
作为最佳实践,最好是解耦数据源和视图控制器。避免将视图控制器做成数据源,这样可以更好地分离和复用各项功能。要从数据源传递给视图控制器,可以使用闭包或委托模式。一个简单的表格视图数据源的典型实现可能如下
class ItemDataSource: NSObject, UITableViewDataSource {
private var items = [Any]()
init(tableView: WKTableView) {
// register cell here so when you need this data source you don't have to repeat this line of code
tableView.register(cell: ItemTableViewCell.self)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath)
let presenter = ItemPresenter(with: items[indexPath.row])
cell.configure(with: presenter)
return cell
}
}
然后在您的视图控制器中使用数据源作为实例变量。
提示: WinkKit
提供了一些现成的数据源类,具有一些常用方法,例如插入/删除/重新加载项目或处理无限滚动。请查看 WKTableViewDataSource
、WKCollectionViewDataSource
和 WKTableViewInfiniteDataSourceDelegate
。
更多工具和相关内容
还有其他类和扩展可以用来实现某些行为
- 类
Logger
: 包含日志记录方法以及避免在发布模式下打印调试信息的方法;OrderedSet
: 类似于Set
但元素是有序的;Queue
: 一个简单的队列类
- 扩展
UIAlertController
: 包含快速显示警告的方法Date
: 包含从字符串和格式创建日期的方法,以及一些获取天、小时的方法Collection
: 包含一个下标,可以访问值即使索引是错误的(它返回一个可选值)CALayer
: 包含为层添加边框的方法UIColor
: 允许使用十六进制字符串创建颜色UIWindow
: 包含获取当前最顶层视图控制器的方法
作者
Rico Crescenzio - Linkedin
授权
本项目的授权协议为 MIT 协议 - 有关详细信息,请参阅 LICENSE 文件