Bagaimana cara menerapkan tipe ke instance NSFetchRequest?

102

Di Swift 2, kode berikut berfungsi:

let request = NSFetchRequest(entityName: String)

tetapi di Swift 3 ini memberikan kesalahan:

Parameter umum "ResultType" tidak dapat disimpulkan

karena NSFetchRequestsekarang menjadi tipe generik. Dalam dokumen mereka, mereka menulis ini:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

jadi jika kelas hasil saya misalnya, Levelbagaimana saya harus meminta dengan benar?

Karena ini tidak berhasil:

let request: NSFetchRequest<Level> = Level.fetchRequest
Deniss
sumber
1
tautan ke fitur baru, di mana saya menemukan kode: developer.apple.com/library/prerelease/content/releasenotes/…
Deniss
1
Ini adalah metode, jadi seharusnyalet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan
5
Atau hanyalet request = Level.fetchRequest()
Martin R
2
@MartinR Itu tidak akan lolos kompilasi karena itu ambigu.
Sulthan
6
@MartinR tampaknya anggota stack overflow sangat mencintaimu. Mereka akan memberi suara Anda secara membabi buta. : P
BangOperator

Jawaban:

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

atau

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

tergantung versi yang Anda inginkan.

Anda harus menentukan tipe generik karena jika tidak, pemanggilan metode akan ambigu.

Versi pertama ditentukan untuk NSManagedObject, versi kedua dibuat secara otomatis untuk setiap objek menggunakan ekstensi, misalnya:

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

    @NSManaged var timeStamp: NSDate?
}

Intinya adalah menghapus penggunaan konstanta String.

Sulthan
sumber
1
Jadi untuk setiap entitas, apakah saya perlu menambahkan kode ekstensi? Atau itu terjadi secara otomatis? Jadi, jika saya memiliki entitas "Anjing" dan entitas "Kucing", apakah saya memerlukan "ekstensi Anjing {@nonobjc ...}" dan "ekstensi Kucing {@nonobjc ...}"?
Dave G
@DaveG Ekstensi itu dibuat untuk Anda secara otomatis.
Sulthan
1
Oke, ty, tapi saya agak bingung karena saya mencoba 'let fetchRequest = NSFetchRequest <myEntityName> (entityName: "myEntityName")' dan saya mendapat pesan error "Use of undeclared type" myEntityName "
Dave G
4
Catatan: Metode fetchRequest () hanya tersedia di iOS 10
dzensik
@Sulthan Hai, Ketika saya mencoba dengan kode Anda, terjadi galat berikut. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov
56

Saya pikir saya berhasil dengan melakukan ini:

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

setidaknya itu menyimpan dan memuat data dari DataBase.

Tapi sepertinya itu bukan solusi yang tepat, tapi berhasil untuk saat ini.

Deniss
sumber
Saya lebih menyukai solusi ini, karena saya dulu memiliki metode tunggal yang mengambil nama entitas sebagai parameter dan baru saja mengirimkan kembali array NSManagedObjects.
n_b
Menyukai ini juga karena tidak perlu membuat kelas khusus. Bisa saja menggunakan nama entitas!
Liam Bolling
Jawaban yang diremehkan. Terima kasih!
David Chelidze
33

Struktur paling sederhana yang saya temukan yang berfungsi di 3.0 adalah sebagai berikut:

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

dimana Jenis entitas datanya adalah Negara.

Namun, ketika mencoba membuat Core Data BatchDeleteRequest, saya menemukan bahwa definisi ini tidak berfungsi dan tampaknya Anda harus menggunakan formulir:

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

meskipun format ManagedObject dan FetchRequestResult seharusnya setara.

Ron Diel
sumber
1
Struktur pertama yang disebutkan dalam jawaban ini adalah satu-satunya cara saya saat ini dapat mengkompilasi dengan pengontrol hasil yang saya ambil di Swift3 / iOS 10 / Xcode 8.
David L
Itulah pengalaman saya setelah mencoba berbagai bentuk. Apakah mereka menutupi bentuk lain dalam presentasi CoreData? Berencana untuk memeriksanya besok ...
Ron Diel
Contoh pertama adalah cara termudah yang saya temukan tanpa harus menggunakan if #available(iOS 10.0) { ... }kondisional
djv
12

Berikut beberapa metode CoreData umum yang mungkin menjawab pertanyaan Anda:

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

Dengan asumsi bahwa ada pengaturan NSManagedObject untuk Kontak seperti ini:

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

Metode ini dapat digunakan dengan cara berikut:

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()
iphaaw.dll
sumber
Bersih dan elegan. Seandainya saya bisa memilih ini hingga 100! Satu sentuhan, bertanya-tanya apa yang Anda pikirkan, saya membungkus setiap metode dengan konteks? .Perform ({}) untuk keamanan utas. Ini direkomendasikan oleh Apple.
Tinkerbell
Tidak terlalu OO. Kecuali Anda dapat menulis ini sebagai ekstensi ke NSManagedObjectContect, itu akan menjadi solusi yang bagus.
karena kapak
1
Perhatikan saja - untuk menghitung semua record yang Anda ambil dan kemudian menghitung jumlah entri array - ini benar-benar tidak efisien. Anda mungkin ingin memperluas fungsi recordsInTable untuk memanfaatkan context.count (request)
muz the ax
Ini adalah tambahan yang bagus dan harus memiliki lebih banyak suara, tetapi mungkin tidak karena itu menyimpang dari pertanyaan utama (meskipun itu berguna). Sesuatu yang saya sarankan untuk diubah dengan fungsi delete, adalah menghapus dengan NSManagedObjectID. Jadi sebelum context.delete(record)menambahkan let record = context.object(with: record.objectID)dan menggunakan objek record untuk menghapus.
PostCodeism
6

Ini adalah cara termudah untuk bermigrasi ke Swift 3.0, cukup tambahkan <Country>

(diuji dan bekerja)

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

Saya juga memiliki "ResultType" tidak bisa menjadi kesalahan yang disimpulkan. Mereka membersihkan setelah saya membangun kembali model data yang mengatur Codegen setiap entitas ke "Definisi Kelas". Saya melakukan penulisan singkat dengan petunjuk langkah demi langkah di sini:

Mencari tutorial yang jelas tentang revisi NSPersistentContainer di Xcode 8 dengan Swift 3

Yang saya maksud dengan "membangun kembali" adalah saya membuat file model baru dengan entri dan atribut baru. Agak membosankan, tapi berhasil!

Michael Garito
sumber
0

Apa yang paling berhasil untuk saya sejauh ini adalah:

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

Saya memiliki masalah yang sama dan saya menyelesaikannya dengan langkah-langkah berikut:

  • Pilih file xcdatamodeld Anda dan buka Pemeriksa Model Data
  • Pilih Entitas pertama Anda dan pergi ke kelas Bagian
  • Pastikan bahwa Codegen "Definisi Kelas" dipilih.
  • Hapus semua file Entitas yang Anda buat. Anda tidak membutuhkannya lagi.

Setelah melakukan itu saya harus menghapus / menulis ulang semua kejadian fetchRequest karena XCode tampaknya entah bagaimana bercampur dengan versi yang dibuat kode.

HTH

Oliver Koehler
sumber
0

Swift 3.0 Ini seharusnya bekerja.

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