Come applicare il tipo a un'istanza NSFetchRequest?


102

In Swift 2 il codice seguente funzionava:

let request = NSFetchRequest(entityName: String)

ma in Swift 3 dà errore:

Non è stato possibile dedurre il parametro generico "ResultType"

perché NSFetchRequestora è un tipo generico. Nei loro documenti hanno scritto questo:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

quindi se la mia classe di risultati è ad esempio Levelcome dovrei richiedere correttamente?

Perché questo non funziona:

let request: NSFetchRequest<Level> = Level.fetchRequest

1
link a nuove funzionalità, dove ho trovato il codice: developer.apple.com/library/prerelease/content/releasenotes/…
Deniss

1
È un metodo, quindi dovrebbe esserelet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan

5
O semplicementelet request = Level.fetchRequest()
Martin R,

2
@MartinR Quello non passerebbe la compilazione perché è ambiguo.
Sulthan

6
@MartinR sembra che i membri dello stack overflow ti amino molto. Ti voteranno ciecamente. : P
BangOperator

Risposte:


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

o

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

a seconda della versione che desideri.

Devi specificare il tipo generico perché altrimenti la chiamata al metodo è ambigua.

La prima versione è definita per NSManagedObject, la seconda versione viene generata automaticamente per ogni oggetto utilizzando un'estensione, ad esempio:

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

    @NSManaged var timeStamp: NSDate?
}

Il punto è rimuovere l'uso delle costanti String.


1
Quindi per ogni entità, devo aggiungere il codice di estensione? O questo avviene automaticamente? Quindi, se ho un'entità "Dog" e un'entità "Cat", ho bisogno di "extension Dog {@nonobjc ...}" e "extension Cat {@nonobjc ...}"?
Dave G,

@DaveG Quell'estensione viene generata automaticamente per te.
Sulthan

1
Ok, ty, ma sono un po 'confuso perché ho provato "let fetchRequest = NSFetchRequest <myEntityName> (entityName:" myEntityName ")" e ho ricevuto l'errore "Use of undeclared type" myEntityName "
Dave G

4
Nota: il metodo fetchRequest () è disponibile solo in iOS 10
dzensik

@Sulthan Ciao, Quando ho provato con il tuo codice, si verifica il seguente errore. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov

56

Penso di averlo fatto funzionare in questo modo:

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

almeno salva e carica i dati da DataBase.

Ma sembra che non sia una soluzione adeguata, ma per ora funziona.


Questa soluzione mi piace di più, poiché avevo un unico metodo che prendeva il nome dell'entità come parametro e restituiva semplicemente un array di NSManagedObjects.
n_b

Mi è piaciuto anche questo perché non richiedeva la creazione di una classe personalizzata. Potrebbe semplicemente usare il nome dell'entità!
Liam Bolling

Risposta sottovalutata. Grazie!
David Chelidze

33

La struttura più semplice che ho trovato che funziona in 3.0 è la seguente:

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

dove il tipo di entità di dati è Paese.

Tuttavia, quando ho provato a creare un Core Data BatchDeleteRequest, ho scoperto che questa definizione non funziona e sembra che dovrai seguire il modulo:

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

anche se i formati ManagedObject e FetchRequestResult dovrebbero essere equivalenti.


1
La prima struttura menzionata in questa risposta è l'unico modo in cui posso attualmente compilare questo con il mio controller dei risultati recuperati su Swift3 / iOS 10 / Xcode 8.
David L

Questa è stata la mia esperienza dopo aver provato varie forme. Coprono altre forme nella presentazione CoreData? Pianifica di controllarlo domani ...
Ron Diel

Il primo esempio è il modo più semplice che ho trovato senza dover usare il if #available(iOS 10.0) { ... }condizionale
djv

12

Ecco alcuni metodi CoreData generici che potrebbero rispondere alla tua domanda:

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

Supponendo che esista una configurazione NSManagedObject per Contact come questa:

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

Questi metodi possono essere utilizzati nel modo seguente:

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

Pulito ed elegante. Vorrei poter votare fino a 100! Un ritocco, chiedendomi cosa ne pensi, ho racchiuso ogni metodo con il contesto? .Perform ({}) per la sicurezza del thread. Questo è consigliato da Apple.
Tinkerbell

Non molto OO. A meno che tu non sia stato in grado di scriverli come un'estensione di NSManagedObjectContect, sarebbe una buona soluzione.
muz the ax

1
Ho appena notato - per contare tutti i record che stai recuperando e quindi contare il numero di voci di array - questo è davvero inefficiente. Probabilmente vorrai espandere la funzione recordsInTable per utilizzare context.count (richiesta)
muz the ax

Queste sono belle aggiunte e dovrebbero avere più voti, ma probabilmente no perché è una digressione dalla domanda principale (anche se è utile). Qualcosa che suggerirei di cambiare con la funzione di eliminazione è invece di eliminare con il file NSManagedObjectID. Quindi, prima di context.delete(record)aggiungere let record = context.object(with: record.objectID)e utilizzare quell'oggetto record per eliminare.
PostCodeism

6

Questo è il modo più semplice per migrare a Swift 3.0, basta aggiungere <Country>

(testato e lavorato)

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

0

Ho anche avuto "ResultType" non poteva essere dedotto errori. Sono stati cancellati una volta che ho ricostruito il modello di dati impostando il Codegen di ciascuna entità su "Definizione classe". Ho scritto un breve articolo con le istruzioni passo passo qui:

Alla ricerca di un tutorial chiaro sul NSPersistentContainer rivisto in Xcode 8 con Swift 3

Con "ricostruito" intendo che ho creato un nuovo file modello con nuove voci e attributi. Un po 'noioso, ma ha funzionato!


0

Quello che ha funzionato meglio per me finora è stato:

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

0

Ho avuto lo stesso problema e l'ho risolto con i seguenti passaggi:

  • Seleziona il tuo file xcdatamodeld e vai a Data Model Inspector
  • Seleziona la tua prima entità e vai alla classe Sezione
  • Assicurati che Codegen "Class Definition" sia selezionato.
  • Rimuovi tutti i tuoi file Entity generati. Non ne hai più bisogno.

Dopo averlo fatto, ho dovuto rimuovere / riscrivere tutte le occorrenze di fetchRequest poiché XCode sembra in qualche modo confondersi con la versione codegenerata.

HTH


0

Swift 3.0 Questo dovrebbe funzionare.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.