タイプを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/...
Deniss

1
それは方法なので、そうであるべきですlet request: NSFetchRequest<Level> = Level.fetchRequest()
スルタン

5
または単にlet request = Level.fetchRequest()
Martin R

2
@MartinRあいまいなため、コンパイルに合格しません。
スルタン

6
@MartinRはスタックオーバーフローのメンバーがあなたをとても愛しているようです。彼らはあなたを盲目的に賛成するでしょう。:P
BangOperator 2016年

回答:


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

または

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

必要なバージョンに応じて。

ジェネリック型を指定する必要があります。指定しないと、メソッドの呼び出しがあいまいになるためです。

最初のバージョンはに対して定義されNSManagedObject、2番目のバージョンは拡張機能を使用してすべてのオブジェクトに対して自動的に生成されます。例:

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

    @NSManaged var timeStamp: NSDate?
}

重要なのは、文字列定数の使用を削除することです。


1
すべてのエンティティについて、拡張コードを追加する必要がありますか?それとも自動的に起こりますか?「Dog」エンティティと「Cat」エンティティがある場合、「extension Dog {@nonobjc ...}」と「extension Cat {@nonobjc ...}」が必要ですか?
Dave G

@DaveGその拡張機能は自動的に生成されます。
2016

1
わかりました、tyですが、 'let fetchRequest = NSFetchRequest <myEntityName>(entityName: "myEntityName")'を試して、「宣言されていない型 "myEntityName"の使用
Dave G

4
注:メソッドfetchRequest()はiOS 10でのみ使用可能です
dzensik

@Sulthanこんにちは、あなたのコードで試したところ、次のエラーが発生しました。Type 'Project Name' does not conform to protocol 'NSFetchRequestResult'
スベトスラフアタナソフ2016年

56

私はこれを行うことでそれを機能させたと思います:

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

少なくとも、データベースからデータを保存およびロードします。

しかし、それは適切な解決策ではないように感じますが、今のところ機能します。


エンティティ名をパラメーターとして受け取り、NSManagedObjectsの配列を返すだけの単一のメソッドを使用していたため、このソリューションの方が好きです。
n_b

カスタムクラスを作成する必要がないため、これも気に入っています。エンティティ名だけを使用できます!
Liam Bolling 2016年

過小評価された答え。ありがとう!
David Chelidze

33

3.0で機能することがわかった最も単純な構造は次のとおりです。

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

ここで、データエンティティのTypeはCountryです。

ただし、Core Data BatchDeleteRequestを作成しようとしたときに、この定義が機能せず、次のフォームを使用する必要があるようです:

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

ManagedObjectとFetchRequestResultの形式は同等であると想定されていますが。


1
この回答に記載された第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)")
    }
}

次のようなContactの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票まで投票できたらいいのに!ワンタッチアップ、あなたが何を考えているのだろうと思って、スレッドの安全性のために各メソッドをcontext?.perform({})でラップしました。これはAppleによって推奨されています。
ティンカーベル2016年

あまりオブジェクト指向ではありません。これらをNSManagedObjectContectの拡張機能として記述できなかった場合は、それが優れたソリューションになります。
muz the ax

1
気がついた-すべてのレコードをカウントしてそれらを取得し、次に配列エントリの数をカウントする-これは本当に非効率的です。おそらくcontext.count(リクエスト)を利用するrecordsInTable機能を拡張したい
斧MUZ

これらは素晴らしい追加であり、より多くの票を投じるべきですが、おそらくそれは主要な質問からの余談であるため(おそらく有用ではありますが)、そうではありません。削除機能で変更することを強く勧めるのは、NSManagedObjectID代わりに削除することです。したがって、context.delete(record)追加する前に、 let record = context.object(with: record.objectID)そのレコードオブジェクトを使用して削除します。
PostCodeism 2018

6

これは、Swift 3.0に移行する最も簡単な方法です。 <Country>

(テストされ、機能しました)

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

0

「ResultType」が推論できないエラーもありました。各エンティティのCodegenを「クラス定義」に設定するデータモデルを再構築すると、それらはクリアされました。私はここでステップバイステップの指示で簡単な記事を書きました:

Swift 3を搭載したXcode 8で改訂されたNSPersistentContainerの明確なチュートリアルを探しています

「再構築」とは、新しいエントリと属性を使用して新しいモデルファイルを作成したことを意味します。少し面倒ですが、うまくいきました!


0

これまでのところ私にとって最も効果的だったのは:

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

0

同じ問題があり、次の手順で解決しました:

  • xcdatamodeldファイルを選択して、データモデルインスペクターに移動します
  • 最初のエンティティを選択し、セクションクラスに移動します
  • Codegen「クラス定義」が選択されていることを確認してください。
  • 生成されたすべてのエンティティファイルを削除します。それらはもう必要ありません。

それを行った後、XCodeがコード生成されたバージョンと混同しているように見えるので、fetchRequestのすべての発生を削除/書き換える必要がありました。

HTH


0

Swift 3.0これは動作するはずです。

let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.