情节提要和服务定位器
关于如何精简视图控制器,进行依赖注入以及从不使用情节提要的文章很多。 我想写一点服务定位器模式如何提供一种轻量级的替代方案,例如使用Coordinators将事物连接在一起。 您仍然可以在情节提要中直观地查看应用程序流程,同时将配置与使用分开。
情节提要的一种未充分利用的技术是通过对象占位符将对象添加到视图控制器:
您可以像连接任何视图插座一样连接对象:
让我们继续定义一个简单的服务定位器:
类PostServiceLocator:NSObject {
懒惰的var dataSource:PostDataSource = {
返回PostDataSource()
}()
}
您可以为所有依赖项使用单个服务定位器,也可以为更复杂的应用程序使用,根据功能组创建单独的实例。
关于从情节提要中初始化对象的一件事要记住的是,它不会调用任何init()方法,而是通过awakeFromNib()方法调用。 我们的PostDataSource代码涉及更多。 首先,让我们创建一个协议:
协议TableViewDataSource {
关联类型ItemType
var个项目:[ItemType] {get}
func load(completion:(error:NSError?)-> Void)
func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell
}
我们定义一个associatedType,以便我们可以添加协议扩展,该扩展知道如何从数据源中选择单个项目:
扩展TableViewDataSource {
func itemInRow(row:Int)-> ItemType {
返回项目[行]
}
}
例如,这将允许从数据源中选择一个项目并将其传递给下一个视图控制器。 可以在github上找到完成实际工作的PostDataSource的代码,本文涉及太多。 那么我们如何在视图控制器中使用服务定位器呢? 我们将其连接如下:
@IBOutlet私人var serviceLocator:PostServiceLocator!
@IBOutlet私有var tableView:UITableView!
覆盖func viewDidLoad(){
super.viewDidLoad()
加载()
}
私人函数load(){
UIApplication.sharedApplication()。networkActivityIndicatorVisible = true
serviceLocator.dataSource.load {[弱自我]错误
推迟{
UIApplication.sharedApplication()。networkActivityIndicatorVisible = false
}
self?.tableView.reloadData()
}
}
如您所见,它非常易于使用。 这也使得充当UITableViewDataSource成为一件麻烦事:
func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回serviceLocator.dataSource.tableView(tableView,numberOfRowsInSection:部分)
}
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell {
返回serviceLocator.dataSource.tableView(tableView,cellForRowAtIndexPath:indexPath)
}
那测试呢? 就个人而言,我还不十分相信视图测试,但是您可以很好地对数据源进行单元测试。 我这样定义数据源初始化程序:
初始化(会话:NSURLSession = NSURLSession.sharedSession())
Swift默认参数使构造函数注入变得容易! 在测试中,我们可以创建一个模拟会话,如下所示:
让会话= MockSession()
让dataSource = PostDataSource(session:会话)
当然,在模拟会话中,我们仅覆盖数据源调用的方法,并对一组固定的数据进行声明。 而且它们的运行速度也很好。
我发现这是一种非常方便的技术。 您可以在github上查看项目示例代码。