Deniss
Deniss

Reputation: 1675

How to apply the type to a NSFetchRequest instance?

In Swift 2 the following code was working:

let request = NSFetchRequest(entityName: String)

but in Swift 3 it gives error:

Generic parameter "ResultType" could not be inferred

because NSFetchRequest is now a generic type. In their documents they wrote this:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

so if my result class is for example Level how should I request correctly?

Because this not working:

let request: NSFetchRequest<Level> = Level.fetchRequest

Upvotes: 106

Views: 50498

Answers (10)

jeka
jeka

Reputation: 1

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()
    
}

Upvotes: 0

Chamath Jeevan
Chamath Jeevan

Reputation: 5172

Swift 3.0 This should work.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate

Upvotes: 1

Oliver Koehler
Oliver Koehler

Reputation: 721

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

Upvotes: 0

letanthang
letanthang

Reputation: 420

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

(tested and worked)

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

Upvotes: 6

iphaaw
iphaaw

Reputation: 7204

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()

Upvotes: 15

benhofmann
benhofmann

Reputation: 341

What worked best for me so far was:

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

Upvotes: 0

Michael Garito
Michael Garito

Reputation: 3005

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!

Upvotes: 0

Ron Diel
Ron Diel

Reputation: 1424

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.

Upvotes: 33

Sulthan
Sulthan

Reputation: 130102

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.

Upvotes: 138

Deniss
Deniss

Reputation: 1675

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.

Upvotes: 56

Related Questions