Як застосувати тип до екземпляра 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.