Як застосувати тип до екземпляра NSFetchRequest?


102

У Swift 2 працював наступний код:

let request = NSFetchRequest(entityName: String)

але у Swift 3 він дає помилку:

Не вдалося зробити загальний параметр "ResultType"

бо NSFetchRequestзараз родовий тип. У своїх документах вони написали це:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

тож якщо, наприклад, мій клас результатів, Levelяк мені правильно запитати?

Тому що це не працює:

let request: NSFetchRequest<Level> = Level.fetchRequest

1
посилання на нові функції, де я знайшов код: developer.apple.com/library/prerelease/content/releasenotes/…
Деніс

1
Це метод, так і має бутиlet request: NSFetchRequest<Level> = Level.fetchRequest()
Султан

5
Або простоlet request = Level.fetchRequest()
Мартін Р

2
@MartinR Це не пройшло компіляцію, оскільки це амбітно.
Султан

6
@MartinR, схоже, члени переповнення стека дуже люблять вас. Вони будуть наосліп вас. : P
BangOperator

Відповіді:


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

або

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

залежно від того, яку версію ви хочете.

Ви повинні вказати загальний тип, оскільки в іншому випадку виклик методу неоднозначний.

Перша версія визначена для NSManagedObject, друга версія генерується автоматично для кожного об'єкта за допомогою розширення, наприклад:

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

    @NSManaged var timeStamp: NSDate?
}

Вся справа в тому, щоб зняти використання констант String.


1
Тому для кожної сутності мені потрібно додати код розширення? Або це відбувається автоматично? Отже, якщо у мене є сутність "Собака" та "Кішка", чи потрібні мені "розширення Собака {@nonobjc ...}" та "Кішка розширення {@nonobjc ...}"?
Дейв Г

@DaveG Це розширення створюється автоматично.
Султан

1
Гаразд, ти, але я трохи розгублений, тому що я спробував 'нехай fetchRequest = NSFetchRequest <myEntityName> (сутністьName: "myEntityName") ", і я отримав помилку" Використання незадекларованого типу "myEntityName"
Дейв G

4
Примітка: метод fetchRequest () доступний лише в iOS 10
dzensik

@Sulthan Привіт! Коли я намагався з вашим кодом, виникає така помилка. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Светослав Атанасов

56

Я думаю, що я працював так:

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

принаймні, це зберігає та завантажує дані з DataBase.

Але відчувається, що це не правильне рішення, але воно працює зараз.


Мені подобається це рішення краще, оскільки я мав єдиний метод, який взяв ім'я сутності як параметр і просто передав назад масив NSManagedObjects.
n_b

Це сподобалось і тому, що для нього не потрібно було створити спеціальний клас. Може просто використовувати ім'я сутності!
Ліам Боллінг

Занижена відповідь. Дякую!
Девід Челідзе

33

Найпростіша структура, яку я виявила, що працює в 3.0, полягає в наступному:

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

де Тип об'єкта даних - Країна.

Коли я намагався створити Core Data BatchDeleteRequest, я виявив, що це визначення не працює, і, здається, вам потрібно буде перейти з формою:

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

навіть якщо формати ManagedObject і FetchRequestResult повинні бути еквівалентними.


1
Перша структура, згадана у цій відповіді, - це єдиний спосіб, коли я можу це зробити для компіляції зі своїм контролером результатів на Swift3 / iOS 10 / Xcode 8.
Девід L,

Це був мій досвід після спроб різних форм. Чи висвітлювали вони будь-які інші форми у презентації CoreData? Плануйте перевірити це завтра ...
Рон Діел

Перший приклад - це найпростіший спосіб, який я знайшов, не використовуючи if #available(iOS 10.0) { ... }умовного
djv

12

Ось декілька загальних методів CoreData, які можуть відповісти на ваше запитання:

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

Якщо припустити, що для контакту існує налаштування NSManagedObject так:

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

Ці методи можна використовувати наступним чином:

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

Чисто та елегантно. Бажаю, щоб я міг проголосувати за це 100! Одним дотиком, цікаво, що ви думаєте, я обгорнув кожен метод контекстом? .Perform ({}) для безпеки потоку. Це рекомендує компанія Apple.
Тінкербелл

Не дуже ОО. Якщо б вам не вдалося написати це як розширення до NSManagedObjectContect, це було б гарним рішенням.
muz the axe

1
Щойно помічено - підрахувати всі записи, які ви їх отримуєте, а потім підрахувати кількість записів масиву - це дійсно неефективно. Ви, ймовірно, хочете розширити функцію recordsInTable, щоб використовувати контекст.count (запит)
muz the axe

Це приємні доповнення та мають мати більше голосів, але, мабуть, не тому, що це відступ від головного питання (навіть якщо це корисно). Щось, що я б запропонував важко змінити за допомогою функції видалення, - це видалити NSManagedObjectIDзамість цього. Тому перед тим, як context.delete(record)додати let record = context.object(with: record.objectID)та використовувати цей об'єкт запису для видалення.
PostCodeism

6

Це найпростіший спосіб перейти на Swift 3.0, просто додайте <Country>

(перевірено та відпрацьовано)

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

0

У мене також "ResultType" не можна було зробити помилки. Вони очистилися, як тільки я відновив модель даних, встановивши Codegen кожної сутності на "Визначення класу". Я зробив короткий опис із покроковими інструкціями тут:

Шукаєте чіткий підручник щодо оновленого NSPersistentContainer у Xcode 8 із Swift 3

Під "перебудованим" я маю на увазі, що я створив новий модельний файл з новими записами та атрибутами. Трохи нудно, але це спрацювало!


0

Що найкраще працювало для мене досі:

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

0

У мене було те саме питання, і я вирішив його наступними кроками:

  • Виберіть файл xcdatamodeld та перейдіть до інспектора моделей даних
  • Виберіть свою першу особу та перейдіть до класу розділу
  • Переконайтесь, що вибрано Codegen "Визначення класу".
  • Видаліть усі створені файли Entity. Вам вони більше не потрібні.

Після цього мені довелося видалити / переписати всі випадки fetchRequest, оскільки XCode, якимось чином, змішується з кодованою версією.

HTH


0

Swift 3.0 Це повинно працювати.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.