如何在 Swift 4 中为 NSFetchedResultsController 编写扩展
How to write an extension for NSFetchedResultsController in Swift 4
我正在尝试为 Swift 中的 NSFetchedResultsController
class 编写一个简单的扩展 4.
这是我的第一次尝试 - 在 Swift 3:
public extension NSFetchedResultsController
{
public func sectionCount() -> Int
{
if self.sections == nil
{
return 0
}
return self.sections!.count
}
}
但是我在 Xcode 9 beta 2 和 Swift 4:
中得到这个编译错误
Extension of a generic Objective-C class cannot access the class's
generic parameters at runtime
我试过其他变体都无济于事。请注意,我可以创建一个扩展绑定到特定类型的 NSManagedObject
匹配 resultType
;但这有一个缺点,我需要为 NSFetchedResultsController
.
使用的每个托管对象类型创建一个扩展
最新的 Swift 4 个文档似乎没有很好地解释这一点。
我想我找到了这个问题的答案。
该功能需要对 Objective-C.
可用
将 @objc
添加到 func
应该可以消除编译错误。至少对我有用!
我们最终得到了子类 (Swift 4)。背后的想法 - 将类型作为参数传递。其余设置操作 NSManagedObject
类型以解决编译错误。
public class FetchedResultsController<T: NSManagedObject>: NSFetchedResultsController<NSManagedObject> {
public convenience init(fetchRequest: NSFetchRequest<NSManagedObject>, 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 { [=10=] as? T }
return result
}
}
扩展:
extension Requests {
public static func all<T: NSManagedObject>(_: T.Type) -> NSFetchRequest<NSManagedObject> {
let request = NSFetchRequest<NSManagedObject>(entityName: T.entityName)
return request
}
public static func ordered<T: NSManagedObject>(by: String, ascending: Bool, _: T.Type) -> NSFetchRequest<NSManagedObject> {
let request = NSFetchRequest<NSManagedObject>(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<IssueList> {
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)
// ...
}
}
Subclass-ing 不好,合成是更好的方法。这是我对此的看法:
public protocol FetchedDataProtocol {
associatedtype T: NSFetchRequestResult
var controller: NSFetchedResultsController<T> { get }
subscript(_ indexPath: IndexPath) -> T { get }
}
这样做可以避免转换和映射:
public extension FetchedDataProtocol {
subscript(_ indexPath: IndexPath) -> T {
return controller.object(at: indexPath)
}
}
然后你将它用作你自己的 class 有下标(和任何你想要的):
public struct MainFetchedData<T: NSFetchRequestResult>: FetchedDataProtocol {
public let controller: NSFetchedResultsController<T>
public init(_ controller: NSFetchedResultsController<T>) {
self.controller = controller
}
}
我正在尝试为 Swift 中的 NSFetchedResultsController
class 编写一个简单的扩展 4.
这是我的第一次尝试 - 在 Swift 3:
public extension NSFetchedResultsController
{
public func sectionCount() -> Int
{
if self.sections == nil
{
return 0
}
return self.sections!.count
}
}
但是我在 Xcode 9 beta 2 和 Swift 4:
中得到这个编译错误Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
我试过其他变体都无济于事。请注意,我可以创建一个扩展绑定到特定类型的 NSManagedObject
匹配 resultType
;但这有一个缺点,我需要为 NSFetchedResultsController
.
最新的 Swift 4 个文档似乎没有很好地解释这一点。
我想我找到了这个问题的答案。 该功能需要对 Objective-C.
可用将 @objc
添加到 func
应该可以消除编译错误。至少对我有用!
我们最终得到了子类 (Swift 4)。背后的想法 - 将类型作为参数传递。其余设置操作 NSManagedObject
类型以解决编译错误。
public class FetchedResultsController<T: NSManagedObject>: NSFetchedResultsController<NSManagedObject> {
public convenience init(fetchRequest: NSFetchRequest<NSManagedObject>, 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 { [=10=] as? T }
return result
}
}
扩展:
extension Requests {
public static func all<T: NSManagedObject>(_: T.Type) -> NSFetchRequest<NSManagedObject> {
let request = NSFetchRequest<NSManagedObject>(entityName: T.entityName)
return request
}
public static func ordered<T: NSManagedObject>(by: String, ascending: Bool, _: T.Type) -> NSFetchRequest<NSManagedObject> {
let request = NSFetchRequest<NSManagedObject>(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<IssueList> {
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)
// ...
}
}
Subclass-ing 不好,合成是更好的方法。这是我对此的看法:
public protocol FetchedDataProtocol {
associatedtype T: NSFetchRequestResult
var controller: NSFetchedResultsController<T> { get }
subscript(_ indexPath: IndexPath) -> T { get }
}
这样做可以避免转换和映射:
public extension FetchedDataProtocol {
subscript(_ indexPath: IndexPath) -> T {
return controller.object(at: indexPath)
}
}
然后你将它用作你自己的 class 有下标(和任何你想要的):
public struct MainFetchedData<T: NSFetchRequestResult>: FetchedDataProtocol {
public let controller: NSFetchedResultsController<T>
public init(_ controller: NSFetchedResultsController<T>) {
self.controller = controller
}
}