驯服iOS中的大量控制器(第1部分)
更新:第2部分现在可用
无论您的iOS应用多么简单,您仍然必须遵循MVC软件架构模式。 MVC模式将应用程序的职责分散在模型,视图和控制器之间。 不幸的是,大多数时候,责任最终落在了控制者的肩膀上。 这种做法导致了所谓的大规模控制器 。
我在IndieDevStock上介绍了“在iOS中驯服大量控制器”。 如果您想观看我的会议视频,请购买远程通行证。
大型控制器是不遵守单一职责原理的视图控制器。 这些控制器可能正在访问数据,调用Web服务,创建UI元素以及执行与控制器没有直接关系的其他任务。 这导致了技术债务和维护方面的噩梦。
这篇文章的主要重点是向您展示一些可用于驯服大型控制器的技术。 我们将从一个非常简单的应用程序“ Grocry ”开始。 杂货应用程序负责跟踪购物清单。 Grocry应用程序的界面如下所示:
即使该应用程序非常简单,但指定的控制器中的代码仍然很繁重。 这是段中的代码片段。
覆盖func viewDidLoad(){
super.viewDidLoad()
initializeCoreDataManager()
//获取请求
let request = NSFetchRequest(entityName:“ ShoppingList”)
request.sortDescriptors = [NSSortDescriptor(key:“ title”,ascending:true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest:请求,managedObjectContext:self.managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)
self.fetchedResultsController.delegate =自我
尝试! self.fetchedResultsController.performFetch()
}
下面实现了负责初始化CoreData的initializeCoreDataManager:
私人函式initializeCoreDataManager(){
警卫让modelURL = NSBundle.mainBundle()。URLForResource(“ GrocryDataModel”,withExtension:“ momd”)else {
fatalError(“未找到GrocryDataModel”)
}
警卫队,让ManagedObjectModel = NSManagedObjectModel(contentsOfURL:modelURL)else {
fatalError(“无法初始化ManagedObjectModel”)
}
让persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel:managedObjectModel)
让fileManager = NSFileManager()
警卫让documentURL = fileManager.URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)。
fatalError(“无法获取文档URL”)
}
让storeURL = documentsURL.URLByAppendingPathComponent(“ Grocry.sqlite”)
打印(storeURL)
尝试! persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType,配置:无,URL:storeURL,选项:无)
让类型= NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType
self.managedObjectContext = NSManagedObjectContext(concurrencyType:类型)
self.managedObjectContext.persistentStoreCoordinator =持久性存储协调器
}
您在屏幕快照中看到的UIAlertView使用以下代码显示:
@IBAction func addShoppingListButtonPressed(){
让alertController = UIAlertController(标题:“杂货店”,消息:“输入购物清单”,preferredStyle:.Alert)
让saveAction = UIAlertAction(title:“ Save”,样式:.Default){(action:UIAlertAction)在
让shoppingNameTextField = alertController.textFields![0]
self.saveShoppingList(shoppingNameTextField.text!)
}
alertController.addTextFieldWithConfigurationHandler {(textField)在
}
alertController.addAction(saveAction)
self.presentViewController(alertController,动画:true,完成:无)
}
而且这个清单还在不断增加。
上面的代码可以正常工作。 但是看起来可能是骗人的。 每次将代码添加到上述控制器时,都会造成技术负担。 这意味着每次您在视图控制器中添加/删除/更新代码时,将花费越来越长的时间,这将导致麻烦。
CoreData堆栈
您可以执行的最明显的重构是将所有CoreData设置代码移出视图控制器,并移到单独的类中。 我们将单独的类称为“ CoreDataManager ”,它的唯一责任是设置CoreData堆栈。
类CoreDataManager:NSObject {
varmanagedObjectObjectContext:NSManagedObjectContext!
覆盖init(){
警卫让modelURL = NSBundle.mainBundle()。URLForResource(“ GrocryDataModel”,withExtension:“ momd”)else {
fatalError(“未找到GrocryDataModel”)
}
.....其他代码初始化CoreData堆栈
}
将CoreData设置移到另一个类后,我们可以简单地从AppDelegate内部对其进行初始化。 实现如下所示:
func应用程序(应用程序:UIApplication,didFinishLaunchingWithOptions launchOptions:[NSObject:AnyObject]?)-> Bool {
//初始化核心数据管理器
让coreDataManager = CoreDataManager()
返回真
}
清洁得多吧! 不仅如此,现在您还可以进入ShoppingListTableViewController并删除所有负责设置CoreData堆栈的代码。 删除代码总是很有趣🙂
数据源:
目前,所有数据源方法都在ShoppingListTableViewController内部实现。 它们是UITableViewDataSource的委托方法。 这些方法包括
覆盖func numberOfSectionsInTableView(tableView:UITableView)-> Int
覆盖func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int
覆盖func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell
不用在ShoppingListTableViewController内部实现方法,我们可以将它们提取到数据源中。 这样,与数据源相关的所有内容都将在一个地方可用,而控制器将无需承担任何责任。
ShoppingListDataSource是我们新的数据源类,负责向UITableView控件提供必要的数据。
class ShoppingListDataSource :NSObject,UITableViewDataSource,ShoppingListDataManagerDelegate {
var manager:ShoppingListDataManager!
var tableView:UITableView!
var cellIdentifier:String!
私有let cellConfigurationHandler:(CellType,ShoppingList)->()
init(管理器:ShoppingListDataManager,tableView:UITableView,cellIdentifier:String,cellConfigurationHandler:(CellType,ShoppingList)->()){
self.manager =经理
self.tableView = tableView
self.cellIdentifier = cellIdentifier
self.cellConfigurationHandler = cellConfigurationHandler
super.init()
self.manager.delegate =自我
}
func numberOfSectionsInTableView(tableView:UITableView)-> Int {
返回self.manager.numberOfSections
}
func shoppingListDataManagerDidInsertShoppingList(indexPath:NSIndexPath){
self.tableView.insertRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)
}
func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回self.manager.numberOfItemsInSection(section)
}
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell {
守卫让细胞= tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier,forIndexPath:indexPath)为? CellType else {
fatalError(“未定义ShoppingListTableViewCell!”)
}
让shoppingList = self.manager.objectAtIndexPath(indexPath)
self.cellConfigurationHandler(cell,shoppingList)
返回单元
}
正如您在上面的代码中看到的那样,ShoppingListDataSource符合UITableViewDataSource协议并实现了所有必需的委托方法。
ShoppingListDataSource的最重要部分是带有多个参数的初始化代码。
init(manager:ShoppingListDataManager,tableView:UITableView,cellIdentifier:String,cellConfigurationHandler:(CellType,ShoppingList)->()){
除了类型为ShoppingListDataManager的管理器参数外,大多数参数都是自解释的。 经理负责与CoreData通信以检索记录并将其提供给数据源。
ShoppingListTableViewController现在可以使用数据源,如以下实现所示:
class ShoppingListTableViewController3:UITableViewController,{
私人var dataSource:ShoppingListDataSource !
私人var manager:ShoppingListDataManager!
varmanagedObjectContext:NSManagedObjectContext!
覆盖func viewDidLoad(){
super.viewDidLoad()
self.manager = ShoppingListDataManager(managedObjectContext:self.managedObjectContext)
self.dataSource = ShoppingListDataSource(管理器:self.manager,tableView:self.tableView,cellIdentifier:“ ShoppingListTableViewCell”,cellConfigurationHandler:{(cell:UITableViewCell,shoppingList:ShoppingList)在
})
self.tableView.dataSource = self.dataSource
}
现在,由于职责已移至ShoppingListDataSource和ShoppingListDataManager类,因此您可以从ShoppingListTableViewController删除与NSFetchedResultsController关联的所有代码。
ShoppingListDataManager初始化代码如下所示:
class ShoppingListDataManager:NSObject,NSFetchedResultsControllerDelegate {
var委托:ShoppingListDataManagerDelegate!
varmanagedObjectContext:NSManagedObjectContext!
var fetchedResultsController:NSFetchedResultsController!
init(managedObjectContext:NSManagedObjectContext){
self.managedObjectContext = managedObjectContext
let request = NSFetchRequest(entityName:ShoppingList.entityName)
request.sortDescriptors = ShoppingList.sortDescriptors
request.predicate = NSPredicate(值:true)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest:请求,managedObjectContext:self.managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)
super.init()
self.fetchedResultsController.delegate =自我
尝试! self.fetchedResultsController.performFetch()
}
如前所述,ShoppingListDataManager负责通过NSFetchedResultsController与CoreData通信。 它唯一需要的参数是对NSManagedObjectContext的引用。
通过引入ShoppingListDataSource和ShoppingListDataManager,我们已经见证了许多代码改进。 我们的ShoppingListTableViewController不再包含数据源和NSFetchedResultsController委托方法。 该代码更好,更精简,更易于理解和更改。
在下一部分中,我们将介绍Swift扩展以及它如何帮助创建可重用和可维护的代码。
下载代码