Jak zastosować typ do wystąpienia NSFetchRequest?


102

W Swift 2 działał następujący kod:

let request = NSFetchRequest(entityName: String)

ale w Swift 3 daje błąd:

Nie można wywnioskować parametru ogólnego „ResultType”

ponieważ NSFetchRequestjest teraz typem ogólnym. W swoich dokumentach napisali to:

let request: NSFetchRequest<Animal> = Animal.fetchRequest

więc jeśli moja klasa wyników to na przykład Level jak mam poprawnie zażądać?

Ponieważ to nie działa:

let request: NSFetchRequest<Level> = Level.fetchRequest

1
link do nowych funkcji, gdzie znalazłem kod: developer.apple.com/library/prerelease/content/releasenotes/ ...
Deniss

1
To metoda, więc powinna byćlet request: NSFetchRequest<Level> = Level.fetchRequest()
Sulthan

5
Lub po prostulet request = Level.fetchRequest()
Martin R

2
@MartinR To nie przejdzie kompilacji, ponieważ jest niejasne.
Sulthan

6
@MartinR wydaje się, że członkowie przepełnienia stosu bardzo Cię kochają. Głosują za tobą na ślepo. : P
BangOperator

Odpowiedzi:


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

lub

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

w zależności od wybranej wersji.

Musisz określić typ ogólny, ponieważ w przeciwnym razie wywołanie metody jest niejednoznaczne.

Pierwsza wersja jest definiowana dla NSManagedObject, druga wersja jest generowana automatycznie dla każdego obiektu wykorzystującego rozszerzenie, np .:

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

    @NSManaged var timeStamp: NSDate?
}

Chodzi o to, aby usunąć użycie stałych typu String.


1
Czy dla każdej jednostki muszę dodać kod rozszerzenia? Czy dzieje się to automatycznie? Jeśli więc mam encje „Dog” i „Cat”, czy potrzebuję „extension Dog {@nonobjc ...}” i „extension Cat {@nonobjc ...}”?
Dave G,

@DaveG To rozszerzenie jest generowane automatycznie.
Sulthan

1
Okay, ty, ale jestem trochę zdezorientowany, ponieważ próbowałem „niech fetchRequest = NSFetchRequest <myEntityName> (entityName:„ myEntityName ”)” i otrzymałem błąd „Użycie niezadeklarowanego typu„ myEntityName ”
Dave G

4
Uwaga: metoda fetchRequest () jest dostępna tylko w iOS 10
dzensik

@Sulthan Cześć, Kiedy próbowałem z twoim kodem, pojawił się następujący błąd. Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
Svetoslav Atanasov

56

Myślę, że udało mi się to, robiąc to:

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

przynajmniej zapisuje i ładuje dane z DataBase.

Ale wydaje się, że nie jest to właściwe rozwiązanie, ale na razie działa.


To rozwiązanie podoba mi się bardziej, ponieważ kiedyś miałem jedną metodę, która przyjmowała nazwę jednostki jako parametr i po prostu przekazała tablicę NSManagedObjects.
n_b

To też mi się podobało, ponieważ nie wymagało tworzenia niestandardowej klasy. Można po prostu użyć nazwy jednostki!
Liam Bolling

Niedoceniona odpowiedź. Dzięki!
David Chelidze

33

Najprostsza struktura, którą znalazłem, która działa w wersji 3.0, jest następująca:

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

gdzie Typ jednostki danych to Kraj.

Podczas próby utworzenia żądania danych podstawowych BatchDeleteRequest stwierdziłem jednak, że ta definicja nie działa i wydaje się, że będziesz musiał przejść z formularzem:

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

mimo że formaty ManagedObject i FetchRequestResult mają być równoważne.


1
Pierwsza struktura wspomniana w tej odpowiedzi to jedyny sposób, w jaki mogę to obecnie skompilować z moim kontrolerem pobranych wyników w Swift3 / iOS 10 / Xcode 8.
David L

Takie było moje doświadczenie po wypróbowaniu różnych form. Czy obejmowały jakieś inne formularze w prezentacji CoreData? Zaplanuj jutro to sprawdzić ...
Ron Diel

Pierwszy przykład to najprostszy sposób, jaki znalazłem bez konieczności używania if #available(iOS 10.0) { ... }warunku
djv

12

Oto kilka ogólnych metod CoreData, które mogą odpowiedzieć na Twoje pytanie:

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

Zakładając, że istnieje konfiguracja NSManagedObject dla Contact w następujący sposób:

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

Metody te można wykorzystać w następujący sposób:

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

Czysty i elegancki. Chciałbym móc zagłosować na 100! Jedno poprawienie, zastanawiasz się, co myślisz, otoczyłem każdą metodę kontekstem? .Perform ({}) w celu zapewnienia bezpieczeństwa wątków. Jest to zalecane przez firmę Apple.
Tinkerbell

Niezbyt OO. O ile nie byłbyś w stanie napisać ich jako rozszerzenia do NSManagedObjectContect, byłoby to miłe rozwiązanie.
odpal siekierą

1
Właśnie zauważyłem - aby policzyć wszystkie rekordy, które je pobierasz, a następnie policzyć liczbę wpisów w tablicy - jest to naprawdę nieefektywne. Prawdopodobnie chcesz rozszerzyć funkcję recordsInTable, aby korzystać z context.count (request)
muz the axe

To fajne dodatki i powinny mieć więcej głosów, ale prawdopodobnie nie, ponieważ jest to dygresja od głównego pytania (nawet jeśli jest przydatna). Coś, co sugerowałbym, że trudno zmienić za pomocą funkcji usuwania, to usunięcie za pomocą NSManagedObjectIDzamiast tego. Więc przed context.delete(record)dodaniem let record = context.object(with: record.objectID)i użyciem tego obiektu rekordu do usunięcia.
PostCodeism

6

To najprostszy sposób migracji do Swift 3.0, wystarczy dodać <Country>

(przetestowane i działające)

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

0

Miałem również „ResultType” nie można wywnioskować błędów. Wyjaśnili, kiedy przebudowałem model danych, ustawiając Codegen każdej jednostki na „Definicję klasy”. Zrobiłem krótki opis z instrukcjami krok po kroku tutaj:

Szukasz przejrzystego samouczka na temat poprawionego NSPersistentContainer w Xcode 8 z Swift 3

Przez „przebudowany” rozumiem, że utworzyłem nowy plik modelu z nowymi wpisami i atrybutami. Trochę żmudne, ale zadziałało!


0

Do tej pory najlepiej działało dla mnie:

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

0

Miałem ten sam problem i rozwiązałem go, wykonując następujące czynności:

  • Wybierz plik xcdatamodeld i przejdź do Inspektora modeli danych
  • Wybierz swoją pierwszą jednostkę i przejdź do klasy sekcji
  • Upewnij się, że wybrano Codegen „Class Definition”.
  • Usuń wszystkie wygenerowane pliki jednostek. Już ich nie potrzebujesz.

Po wykonaniu tej czynności musiałem usunąć / przepisać wszystkie wystąpienia fetchRequest, ponieważ XCode wydaje się w jakiś sposób mieszać z wersją kodowaną.

HTH


0

Swift 3.0 To powinno działać.

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.