web-dev-qa-db-fra.com

Comment appliquer le type à une instance NSFetchRequest?

Dans Swift 2, le code suivant fonctionnait:

let request = NSFetchRequest(entityName: String)

mais dans Swift 3 cela donne une erreur:

Le paramètre générique "ResultType" n'a pas pu être déduit

parce que NSFetchRequest est maintenant un type générique. Dans leurs documents, ils ont écrit ceci:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

donc, si ma classe de résultats est par exemple Level, comment demander correctement?

Parce que ça ne marche pas:

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

ou

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

selon la version que vous voulez.

Vous devez spécifier le type générique, sinon l'appel de la méthode est ambigu.

La première version est définie pour NSManagedObject, la deuxième version est générée automatiquement pour chaque objet utilisant une extension, par exemple:

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

    @NSManaged var timeStamp: NSDate?
}

Le but est de supprimer l'utilisation des constantes de chaîne.

126
Sulthan

Je pense que ça marche en faisant ça:

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

au moins, il enregistre et charge les données depuis la base de données.

Mais on a l'impression que ce n'est pas une solution appropriée, mais cela fonctionne pour le moment.

53
Deniss

La structure la plus simple que j'ai trouvée et qui fonctionne dans la version 3.0 est la suivante:

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

où le type d'entité de données est Pays. 

En essayant de créer un Core Data BatchDeleteRequest, cependant, j'ai constaté que cette définition ne fonctionnait pas et il semble que vous deviez utiliser le formulaire:

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

même si les formats ManagedObject et FetchRequestResult sont supposés être équivalents.

33
Ron Diel

Voici quelques méthodes CoreData génériques qui pourraient répondre à votre 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)")
    }
}

En supposant qu'il existe une configuration NSManagedObject pour Contact semblable à celle-ci:

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

Ces méthodes peuvent être utilisées de la manière suivante:

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

C’est le moyen le plus simple de migrer vers Swift 3.0, il suffit d’ajouter <Country>

(testé et travaillé)

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

J'ai aussi eu "ResultType" ne pouvait pas être déduit des erreurs. Ils ont effacé une fois que j'ai reconstruit le modèle de données en définissant Codegen de chaque entité sur "Définition de classe". J'ai fait une brève écriture avec des instructions étape par étape ici:

Vous recherchez un didacticiel clair sur le NSPersistentContainer révisé dans Xcode 8 avec Swift 3

Par "reconstruit", je veux dire que j'ai créé un nouveau fichier de modèle avec de nouvelles entrées et de nouveaux attributs. Un peu fastidieux, mais cela a fonctionné!

0
Michael Garito

J'ai eu le même problème et je l'ai résolu en procédant comme suit:

  • Sélectionnez votre fichier xcdatamodeld et accédez à l'inspecteur de modèles de données.
  • Sélectionnez votre première entité et allez à la section Section 
  • Assurez-vous que Codegen "Définition de classe" est sélectionné.
  • Supprimez tous vos fichiers d'entité générés. Vous n'en avez plus besoin. 

Après cela, j'ai dû supprimer/réécrire toutes les occurrences de fetchRequest car XCode semble se confondre avec la version générée par code. 

HTH

0
Oliver Koehler

Ce qui a le mieux fonctionné pour moi jusqu'à présent était:

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

Swift 3.0 Cela devrait fonctionner.

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