如何将该类型应用于 NSFetchRequest 实例?

在 Swift 2中,以下代码正在工作:

let request = NSFetchRequest(entityName: String)

但在 Swift 3中,它出现了错误:

无法推断泛型参数“ ResultType”

因为 NSFetchRequest现在是一种通用类型,他们在文档中写道:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

那么,如果我的结果类是例如 Level我应该如何正确地请求?

因为这样不行:

let request: NSFetchRequest<Level> = Level.fetchRequest
48683 次浏览

I think i got it working by doing this:

let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Level")

at least it saves and loads data from DataBase.

But it feels like it is not a proper solution, but it works for now.

let request: NSFetchRequest<NSFetchRequestResult> = Level.fetchRequest()

or

let request: NSFetchRequest<Level> = Level.fetchRequest()

depending which version you want.

You have to specify the generic type because otherwise the method call is ambiguous.

The first version is defined for NSManagedObject, the second version is generated automatically for every object using an extension, e.g:

extension Level {
@nonobjc class func fetchRequest() -> NSFetchRequest<Level> {
return NSFetchRequest<Level>(entityName: "Level");
}


@NSManaged var timeStamp: NSDate?
}

The whole point is to remove the usage of String constants.

The simplest structure I found that works in 3.0 is as follows:

let request = NSFetchRequest<Country>(entityName: "Country")

where the data entity Type is Country.

When trying to create a Core Data BatchDeleteRequest, however, I found that this definition does not work and it seems that you'll need to go with the form:

let request: NSFetchRequest<NSFetchRequestResult> = Country.fetchRequest()

even though the ManagedObject and FetchRequestResult formats are supposed to be equivalent.

I also had "ResultType" could not be inferred errors. They cleared once I rebuilt the data model setting each entity's Codegen to "Class Definition". I did a brief writeup with step by step instructions here:

Looking for a clear tutorial on the revised NSPersistentContainer in Xcode 8 with Swift 3

By "rebuilt" I mean that I created a new model file with new entries and attributes. A little tedious, but it worked!

What worked best for me so far was:

let request = Level.fetchRequest() as! NSFetchRequest<Level>

Here are some generic CoreData methods that might answer your question:

import Foundation
import Cocoa


func addRecord<T: NSManagedObject>(_ type : T.Type) -> T
{
let entityName = T.description()
let context = app.managedObjectContext
let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
let record = T(entity: entity!, insertInto: context)
return record
}


func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int
{
let recs = allRecords(T.self)
return recs.count
}




func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}


func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
if let predicate = search
{
request.predicate = predicate
}
if let sortDescriptors = multiSort
{
request.sortDescriptors = sortDescriptors
}
else if let sortDescriptor = sort
{
request.sortDescriptors = [sortDescriptor]
}


do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}




func deleteRecord(_ object: NSManagedObject)
{
let context = app.managedObjectContext
context.delete(object)
}


func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)
{
let context = app.managedObjectContext


let results = query(T.self, search: search)
for record in results
{
context.delete(record)
}
}


func saveDatabase()
{
let context = app.managedObjectContext


do
{
try context.save()
}
catch
{
print("Error saving database: \(error)")
}
}

Assuming that there is a NSManagedObject setup for Contact like this:

class Contact: NSManagedObject
{
@NSManaged var contactNo: Int
@NSManaged var contactName: String
}

These methods can be used in the following way:

let name = "John Appleseed"


let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name


let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %@", name))
for contact in contacts
{
print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")
}


deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %@", name))


recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")


saveDatabase()

This is the simplest way to migrate to Swift 3.0, just add <Country>

(tested and worked)

let request = NSFetchRequest<Country>(entityName: "Country")

I had the same issue and I solved it with the following steps:

  • Select your xcdatamodeld file and go to the Data Model Inspector
  • Select your first Entity and go to Section class
  • Make sure that Codegen "Class Definition" is selected.
  • Remove all your generated Entity files. You don't need them anymore.

After doing that I had to remove/rewrite all occurences of fetchRequest as XCode seem to somehow mix up with the codegenerated version.

HTH

Swift 3.0 This should work.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext


func loadItemsCategory() {


let request: NSFetchRequest<Category> = Category.fetchRequest()
    

do {
categoryArray = try context.fetch(request)
} catch {
print(error)
}
    

tableView.reloadData()
    

}