如何在Swift 4中编写NSFetchedResultsController的扩展
我正在尝试在Swift 4中为NSFetchedResultsController
类编写一个简单的扩展。
这是我的第一次尝试 – 在Swift 3中有效:
public extension NSFetchedResultsController { public func sectionCount() -> Int { if self.sections == nil { return 0 } return self.sections!.count } }
但是我在Swift 4的Xcode 9 beta 2中得到了这个编译错误:
genericsObjective-C类的扩展无法在运行时访问类的generics参数
我尝试过其他变种无济于事。 请注意,我可以创建绑定到与resultType
匹配的特定类型的NSManagedObject
的扩展名; 但是这样做的缺点是我需要为NSFetchedResultsController
使用的每个托管对象类型创建一个扩展。
最新的Swift 4文档似乎不能很好地解释这一点。
我想我找到了这个答案。 该function需要可用于Objective-C。
将@objc
添加到func
应该删除编译错误。 至少它对我有用!
我们最终得到了子类(Swift 4)。 背后的想法 – 传递类型作为参数。 其余的安装程序运行NSManagedObject
类型以解决编译错误。
public class FetchedResultsController: NSFetchedResultsController { public convenience init(fetchRequest: NSFetchRequest , context: NSManagedObjectContext, _: T.Type) { self.init(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) } public var numberOfSections: Int { return sections?.count ?? 0 } public var numberOfFetchedEntities: Int { return fetchedObjects?.count ?? 0 } public func entity(at indexPath: IndexPath) -> T { if let value = object(at: indexPath) as? T { return value } else { fatalError() } } public func entity(at row: Int) -> T { let ip = IndexPath(item: row, section: 0) return entity(at: ip) } public var fetchedEntities: [T] { let result = (fetchedObjects ?? []).compactMap { $0 as? T } return result } }
扩展:
extension Requests { public static func all(_: T.Type) -> NSFetchRequest { let request = NSFetchRequest (entityName: T.entityName) return request } public static func ordered(by: String, ascending: Bool, _: T.Type) -> NSFetchRequest { let request = NSFetchRequest (entityName: T.entityName) request.sortDescriptors = [NSSortDescriptor(key: by, ascending: ascending)] return request } } extension NSManagedObject { public static var entityName: String { let className = NSStringFromClass(self) // As alternative can be used `self.description()` or `String(describing: self)` let entityName = className.components(separatedBy: ".").last! return entityName } public static var entityClassName: String { let className = NSStringFromClass(self) return className } }
用法:
extension ParentChildRelationshipsMainController { private func setupFetchedResultsController() -> FetchedResultsController { let request = DB.Requests.ordered(by: #keyPath(IssueList.title), ascending: true, IssueList.self) let result = FetchedResultsController(fetchRequest: request, context: PCDBStack.shared.mainContext, IssueList.self) return result } } extension ParentChildRelationshipsMainController: NSTableViewDataSource, NSTableViewDelegate { func numberOfRows(in tableView: NSTableView) -> Int { return frController.numberOfFetchedEntities } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let rowID = NSUserInterfaceItemIdentifier("rid:generic") let item = frController.entity(at: row) // ... } }
子类化不好,组合是一种更好的方法。 以下是我对此的看法:
public protocol FetchedDataProtocol { associatedtype T: NSFetchRequestResult var controller: NSFetchedResultsController { get } subscript(_ indexPath: IndexPath) -> T { get } }
这样做可以避免强制转换和映射:
public extension FetchedDataProtocol { subscript(_ indexPath: IndexPath) -> T { return controller.object(at: indexPath) } }
然后你将它用作你自己的下标(以及你想要的)类:
public struct MainFetchedData: FetchedDataProtocol { public let controller: NSFetchedResultsController public init(_ controller: NSFetchedResultsController ) { self.controller = controller } }