質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Core Data

Core DataはAppleのOS X and iOSのためのオブジェクトモデリングと持続性を持ったフレームワークです。Xcodeはエンティティー、属性そして関係性を特定するためのオブジェクトモデルの編集機能を提供します。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

1073閲覧

CoreDataのNSBatchInsertRequestでリレーションシップを設定したい

yodo

総合スコア28

Core Data

Core DataはAppleのOS X and iOSのためのオブジェクトモデリングと持続性を持ったフレームワークです。Xcodeはエンティティー、属性そして関係性を特定するためのオブジェクトモデルの編集機能を提供します。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2022/08/06 03:12

編集2022/08/06 07:17

複数のBookのデータをサーバーから取得し、userというリレーションシップに同一のUserを設定してCoreDataに保存する、という処理をバッチで行いたいと考えています。

しかし、ループ内でリレーションシップを設定しようとすると、このようなエラーが出てしまいます。

Illegal attempt to establish a relationship 'user' between objects in different contexts (source = <Book: 0x10dfa13a0> (entity: Book; id: 0x11c44fa80 <x-coredata:///Book/t3BB93B8D-448D-4C3E-A915-7F5CB558EA993>; data: { ... }) , destination = <User: 0x10dfc4b80> (entity: User; id: 0x138a5f1d0 <x-coredata:///User/t3BB93B8D-448D-4C3E-A915-7F5CB558EA992>; data: { ... }))

「UserとBookが別のコンテクスト」と書かれれていますが、以下のコードを見てもどこで別のコンテクストになっているのかが分かりません。

そもそもバッチでリレーションシップを設定することは可能でしょうか?

該当のソースコード

swift

1func importBooks(user: User) async throws { 2 let books: [BookProperties] = await ... 3 4 let userObjectID = user.objectID 5 let taskContext = persistentContainer.newBackgroundContext() 6 await taskContext.perform { 7 let user2 = taskContext.object(with: userObjectID) 8 let batchInsertRequest = newBatchInsertRequest(propertyList: [BookProperties], user: user2) 9 if let fetchResult = try? taskContext.execute(batchInsertRequest), 10 let batchInsertResult = fetchResult as? NSBatchInsertResult, 11 let success = batchInsertResult.result as? Bool, success { 12 return 13 } 14 } 15} 16 17private func newBatchInsertRequest(propertyList: [BookProperties], user: User) -> NSBatchInsertRequest { 18 var index = 0 19 let total = propertyList.count 20 21 let batchInsertRequest = NSBatchInsertRequest(entity: Book.entity(), managedObjectHandler: { managedObject in 22 guard index < total else { return true } 23 if let book = managedObject as? Book { 24 book.title = propertyList[index].title 25 book.user = user // << Error 26 } 27 index += 1 28 return false 29 }) 30 return batchInsertRequest 31}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

newBatchInsertRequest(propertyList:user:)メソッドではobjectIDを介していないのが原因です。

ドキュメントには記載がないですが動作するためにはNSBatchInsertRequestが独自にNSMangedObjectControllerを作成する必要があるのでNSBatchInsertRequestから渡されるNSManagedObjectはそのNSManagedObjectConrollerに属しています。
そのため外部から持ち込んだNSManagedObjectをリレーション対象として使用できません。

まずNSBatchInsertRequestから渡されたNSMnagedObjectからmanagedObjectContextプロパティを用いてそれが所属するNSManagedObjectControllerを取得します。
次にuserのObjectIDを利用しそのNSManagedObjectControllerから対応するuserのNSManagedObjectを取得します
最後に新たに取得したuserをリレーション対象として指定します。

投稿2022/08/20 00:50

MasakiHori

総合スコア3384

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yodo

2022/08/22 12:06

NSBatchInsertRequestのmanagedObjectHandlerで渡されるNSManagedObjectですが、.managedObjectContextがnilとなっており、所属するNSManagedObjectContextが取得できなそうです
guest

0

ベストアンサー

昨日からCoreDataの学習をしておりますが、回答してみます・・

そもそもバッチでリレーションシップを設定することは可能でしょうか?

ちょっと試している感じですとダメそうですね・・


https://stackoverflow.com/questions/60071578/
https://developer.apple.com/forums/thread/118237
https://developer.apple.com/forums/thread/125374
https://developer.apple.com/documentation/coredata/nsbatchinsertrequest/3333235-init

リンク先にあるように、辞書の配列を使ってエンティティのプロパティを指定する感じにして、
newBackgroundContext()を使わずにviewContextを使う感じで試してみました。
すると、質問欄にあるようなエラーは発生せずにエンティティの追加ができました。
ですが、(エンティティは追加できたものの)リレーションシップのところは設定できていませんでした・・


NSBatchUpdateRequestならリレーションシップの設定ができるのかな?と思いまして、
これも試してみました。
次のようなエラーになりました・・

2022-08-08 22:19:48.378067+0900 CoreDataSwiftUIProduct[90968:4815284] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid relationship ((<NSRelationshipDescription: 0x600000164500>), name relationship, isOptional 1, isTransient 0, entity UserEntity, renamingIdentifier relationship, validation predicates ( ), warnings ( ), versionHashModifier (null) userInfo { }, destination entity GroupEntity, inverseRelationship (null), minCount 0, maxCount 1, isOrdered 0, deleteRule 1) passed to propertiesToUpdate:'

Archiveのドキュメントですので、Swiftのようなたくさん変更のある言語で意味があるかわからないですが、次のような記載がありました。
*「関係(リレーションシップ)の変更はできません」と記載されていますね・・

The goal of a batch update is to change one or more properties on a specific entity that is stored in a persistent store in Core Data. A batch update cannot be used to alter relationships, delete entities, or create new entities. To start a batch update, you create an NSBatchUpdateRequest object which has a number of similarities to an NSFetchRequest.
(機械翻訳)バッチアップデートの目的は、Core Dataの永続ストアに保存されている特定のエンティティの1つまたは複数のプロパティを変更することです。バッチアップデートは、関係の変更、エンティティの削除、または新しいエンティティの作成には使用できません。バッチアップデートを開始するには、NSFetchRequest と多くの類似点を持つ NSBatchUpdateRequest オブジェクトを作成します。
https://developer.apple.com/library/archive/featuredarticles/CoreData_Batch_Guide/BatchUpdates/BatchUpdates.html

投稿2022/08/08 14:00

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問