2016-06-14 28 views
86

Nel Swift 2 seguente codice funzionava:Come applicare il tipo a un'istanza NSFetchRequest?

let request = NSFetchRequest(entityName: String) 

ma in Swift 3 dà errore:

Generic parameter "ResultType" could not be inferred

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

let request: NSFetchRequest<Animal> = Animal.fetchRequest 

quindi se la mia classe risultato è ad esempio Level come dovrei richiedere correttamente?

Perché questo non funziona:

let request: NSFetchRequest<Level> = Level.fetchRequest 
+0

Qual è la descrizione dell'errore? – LightMan

+0

parametro generico "ResultType" non può essere dedotto – Deniss

+1

collegamento a nuove funzionalità, dove ho trovato il codice: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatNewCoreData2016/index.html – Deniss

risposta

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

o

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

a seconda di quale versione si desidera.

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

La prima versione è definito per NSManagedObject, la seconda versione viene generato automaticamente per ogni oggetto utilizzando un'estensione, per esempio:

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

    @NSManaged var timeStamp: NSDate? 
} 

Il punto è quello di eliminare l'uso delle costanti stringa.

+1

Quindi, per ogni entità, devo aggiungere il codice di estensione? O quello succede automaticamente? Quindi, se ho un'entità "Cane" e un'entità "Cat", ho bisogno di "estensione Dog {@nonobjc ...}" e "estensione Cat {@nonobjc ...}"? –

+0

@DaveG Questa estensione viene generata automaticamente. – Sulthan

+1

Ok, ty, ma sono un po 'confuso perché ho provato' let fetchRequest = NSFetchRequest (entityName: "myEntityName") 'e ho ricevuto l'errore "Uso di tipo non dichiarato" myEntityName " –

52

Penso che ho ottenuto che funziona in questo modo:

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

almeno è salva e carica i dati dal database.

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

+0

Mi piace questa soluzione meglio, in quanto avevo un unico metodo che ha preso il nome dell'entità come parametro e ha appena passato un array di NSManagedObjects. –

+0

Piaciuto anche a questo perché non è necessario creare una classe personalizzata. Potrebbe semplicemente usare il nome dell'entità! –

+0

Questa risposta è buona di altre :) –

32

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

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

in cui il tipo di entità dati è Paese.

Quando si cerca di creare un Data BatchDeleteRequest core, tuttavia, ho trovato che questa definizione non funziona e sembra che avrete bisogno di andare con la forma:

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

anche se il ManagedObject e FetchRequestResult i formati dovrebbero essere equivalenti.

+1

La prima struttura menzionata in questa risposta è l'unico modo che posso attualmente ottenere per compilare con il mio controller risultati recuperato su Swift3/iOS 10/Xcode 8 –

+0

Questa è stata la mia esperienza dopo aver provato varie forme. Coprono altri moduli nella presentazione CoreData? Pianifica di provarlo domani ... –

+0

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

0

Ho anche avuto "ResultType" non è stato possibile dedurre errori. Hanno cancellato una volta che ho ricostruito il modello di dati impostando Codegen di ciascuna entità su "Class Definition". Ho fatto una breve resoconto scritto con istruzioni passo passo qui:

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

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

+0

Quindi, sembra un bug in Modello Xcode? – Shadowfax

0

quello che ha funzionato meglio per me finora è stato:

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

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 ci sia un setup NSManagedObject per contatto come questo :

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

Pulito ed elegante. Vorrei poter votare questo su 100! Un ritocco, chiedendo cosa ne pensi, ho avvolto ogni metodo con contesto? .perform ({}) per la sicurezza dei thread. Questo è raccomandato da Apple. – Tinkerbell

+0

puoi fornire lo stesso per l'aggiornamento anche –

+0

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

6

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

(testati e lavorato)

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

ho avuto lo stesso problema e ho risolto con i seguenti passi :

  • selezionare il file xcdatamodeld e selezionare il modello di dati ispettore
  • Selezionare la prima entità e passare alla sezione di sezione
  • Assicurarsi che Codegen "Definizione di classe" sia selezionato.
  • Rimuovi tutti i file Entity generati. Non hai più bisogno di loro.

Dopo averlo fatto ho dovuto rimuovere/riscrivere tutte le occorrenze di fetchRequest come 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