¿Cómo aplicar el tipo a una instancia NSFetchRequest?

102

En Swift 2 funcionaba el siguiente código:

let request = NSFetchRequest(entityName: String)

pero en Swift 3 da error:

No se pudo inferir el parámetro genérico "ResultType"

porque NSFetchRequestahora es un tipo genérico. En sus documentos escribieron esto:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

entonces, si mi clase de resultado es, por ejemplo, Level¿cómo debo solicitar correctamente?

Porque esto no funciona:

let request: NSFetchRequest<Level> = Level.fetchRequest
Deniss
fuente
1
enlace a nuevas funciones, donde encontré el código: developer.apple.com/library/prerelease/content/releasenotes/…
Deniss
1
Es un método, por lo que debería serlet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan
5
O simplementelet request = Level.fetchRequest()
Martin R
2
@MartinR Eso no aprobaría la compilación porque es ambiguo.
Sulthan
6
@MartinR parece que los miembros de stack overflow te quieren mucho. Te votarán a ciegas. : P
BangOperator

Respuestas:

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

o

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

dependiendo de la versión que desee.

Debe especificar el tipo genérico porque, de lo contrario, la llamada al método es ambigua.

La primera versión está definida para NSManagedObject, la segunda versión se genera automáticamente para cada objeto usando una extensión, por ejemplo:

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

    @NSManaged var timeStamp: NSDate?
}

El objetivo es eliminar el uso de constantes de cadena.

Sulthan
fuente
1
Entonces, para cada entidad, ¿necesito agregar un código de extensión? ¿O eso sucede automáticamente? Entonces, si tengo una entidad "Perro" y una entidad "Gato", ¿necesito "extensión Perro {@nonobjc ...}" y "extensión Gato {@nonobjc ...}"?
Dave G
@DaveG Esa extensión se genera automáticamente.
Sulthan
1
De acuerdo, ty, pero estoy un poco confundido porque intenté 'let fetchRequest = NSFetchRequest <myEntityName> (entityName: "myEntityName")' y recibí el error "Uso del tipo no declarado" myEntityName "
Dave G
4
Nota: El método fetchRequest () solo está disponible en iOS 10
dzensik
@Sulthan Hola, Cuando intenté con tu código, se produjo el siguiente error. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov
56

Creo que lo hice funcionar haciendo esto:

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

al menos guarda y carga datos de DataBase.

Pero parece que no es una solución adecuada, pero funciona por ahora.

Deniss
fuente
Me gusta más esta solución, ya que solía tener un único método que tomaba el nombre de la entidad como parámetro y simplemente devolvía una matriz de NSManagedObjects.
n_b
También me gustó esto porque no requería crear una clase personalizada. ¡Podría usar el nombre de la entidad!
Liam Bolling
Respuesta subestimada. ¡Gracias!
David Chelidze
33

La estructura más simple que encontré que funciona en 3.0 es la siguiente:

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

donde el tipo de entidad de datos es País.

Sin embargo, al intentar crear un BatchDeleteRequest de datos básicos, descubrí que esta definición no funciona y parece que deberá seguir el formulario:

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

aunque se supone que los formatos ManagedObject y FetchRequestResult son equivalentes.

Ron Diel
fuente
1
La primera estructura mencionada en esta respuesta es la única forma en que actualmente puedo hacer que esto se compile con mi controlador de resultados obtenidos en Swift3 / iOS 10 / Xcode 8.
David L
Esa fue mi experiencia después de probar varias formas. ¿Cubrieron otros formularios en la presentación de CoreData? Planea verlo mañana ...
Ron Diel
El primer ejemplo es la forma más sencilla que he encontrado sin tener que usar el if #available(iOS 10.0) { ... }condicional
djv
12

Aquí hay algunos métodos CoreData genéricos que pueden responder a su pregunta:

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

Suponiendo que hay una configuración de NSManagedObject para Contact como esta:

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

Estos métodos se pueden utilizar de la siguiente manera:

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()
Ifaaw
fuente
Limpio y elegante. ¡Ojalá pudiera votar esto por 100! Un retoque, preguntándome qué piensas, envolví cada método con contexto. Esto es recomendado por Apple.
Tinkerbell
No muy OO. A menos que pudiera escribirlos como una extensión de NSManagedObjectContect, esa sería una buena solución.
muz the axe
1
Me acabo de dar cuenta: para contar todos los registros los está recuperando y luego contando el número de entradas de matriz, esto es realmente ineficiente. Probablemente desee expandir la función recordsInTable para utilizar context.count (solicitud)
muz the axe
Estas son buenas adiciones y deberían tener más votos, pero probablemente no lo hagan porque es una digresión de la pregunta principal (aunque es útil). Algo que sugeriría cambiar con la función de eliminación es eliminar con el NSManagedObjectID. Entonces, antes de context.delete(record)agregar let record = context.object(with: record.objectID)y usar ese objeto de registro para eliminar.
Postcodeísmo
6

Esta es la forma más sencilla de migrar a Swift 3.0, solo agregue <Country>

(probado y trabajado)

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

También tuve "ResultType" no se pudieron inferir errores. Se borraron una vez que reconstruí el modelo de datos estableciendo el Codegen de cada entidad en "Definición de clase". Hice una breve reseña con instrucciones paso a paso aquí:

Buscando un tutorial claro sobre el NSPersistentContainer revisado en Xcode 8 con Swift 3

Por "reconstruido" me refiero a que creé un nuevo archivo de modelo con nuevas entradas y atributos. Un poco tedioso, ¡pero funcionó!

Miguel Garito
fuente
0

Lo que funcionó mejor para mí hasta ahora fue:

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

Tuve el mismo problema y lo resolví con los siguientes pasos:

  • Seleccione su archivo xcdatamodeld y vaya al Inspector del modelo de datos
  • Seleccione su primera entidad y vaya a la clase de sección
  • Asegúrese de que la "Definición de clase" de Codegen esté seleccionada.
  • Elimina todos tus archivos de entidad generados. Ya no los necesita.

Después de hacer eso, tuve que eliminar / reescribir todas las apariciones de fetchRequest ya que XCode parece confundirse de alguna manera con la versión generada por código.

HTH

Oliver Koehler
fuente
0

Swift 3.0 Esto debería funcionar.

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