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

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

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

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

Swift

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

Q&A

解決済

2回答

1955閲覧

SwiftUIでのCore Dataの処理を共通化したいが、エラーが出る

Potewo

総合スコア27

Core Data

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

Swift

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

0グッド

0クリップ

投稿2020/06/25 00:54

編集2020/06/25 01:00

前提・実現したいこと

SwiftUIとCore Dataを使ったToDoアプリを作っています。

作っていく中でアプリの複数のViewからCore Dataにアクセスすることになったので、Core Dataへの追加や削除、情報の取得、保存などを共通化したいと思いました。(そのViewというのは具体的には、ToDoのタスクの一覧を表示するView、一覧のそれぞれの行のView、タスクの詳細を表示・編集するView、データを更新するボタンのViewなどです。)

変更を加えたものを保存するのはどのViewからも呼び出すのですが、それぞれのViewの中に全く同じ保存する関数を書くのは無駄な気がします。

そこで、別のファイルにCore Dataの処理をするクラスを書き、それぞれのViewからそのクラスへアクセスして保存や追加などをできるようにしたいと考えました。しかし、エラーが出てしまい、実行できませんでした。
何が理由でエラーが出てしまったのでしょうか。また、この別のクラスに書くというのが正しいのでしょうか。(Environmentなどを使った方がいいのでしょうか)
よろしくお願いします。

発生している問題・エラーメッセージ

ビルドするときには何もエラーが出ないのですが、シミュレーターで実行するとForEachの行で以下のように表示されます。iPhoneの実機でも同様に表示されました。

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

該当のソースコード

問題が発生したプロジェクトは大きすぎたため、新しくプロジェクトを作成して、問題が発生している部分だけを書きました。

ContentView.swift

swift

1import SwiftUI 2 3struct ContentView: View { 4 var toDoHelper = ToDoHelper() 5 @State var taskName: String = "" 6 var body: some View { 7 List { 8 HStack { 9 TextField(taskName, text: $taskName) 10 Spacer() 11 Button(action: addTask) { 12 Image(systemName: "plus.circle") 13 } 14 } 15 16 ForEach(toDoHelper.tasks, id: .self) { task in 17 Text(task.name ?? "") 18 } 19 } 20 } 21 22 func addTask() { 23 toDoHelper.add(taskName: self.taskName) 24 taskName = "" 25 } 26}

ToDoHelper.swift

swift

1import SwiftUI 2import CoreData 3 4class ToDoHelper { 5 @Environment(.managedObjectContext) var context 6 @FetchRequest(entity: ToDo.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ToDo.createdAt, ascending: true)]) var tasks: FetchedResults<ToDo> 7 func save() { 8 do { 9 try self.context.save() 10 } catch { 11 print(error) 12 } 13 } 14 15 func add(taskName: String) { 16 let newTask = ToDo(context: self.context) 17 newTask.id = UUID() 18 newTask.name = taskName 19 newTask.createdAt = Date() 20 save() 21 } 22}

CoreDataはToDoというエンティティの中に、Date型のcreatedAtとUUID型のid、String型のnameがAttributesに設定されています。

試したこと

classをstructにしてみたり、ObservableObjectにしてみたりしたのですが、解決しませんでした。また、似たような事例を探してみたのですが、見つかりませんでした。
ToDoHelperクラスの中の処理をそのままContenteView構造体の中に書くと問題なく動きます。
以下は正常に動いたプログラムです。

swift

1import SwiftUI 2 3struct ContentView: View { 4 @Environment(.managedObjectContext) var context 5 @FetchRequest(entity: ToDo.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ToDo.createdAt, ascending: true)]) var tasks: FetchedResults<ToDo> 6 var toDoHelper = ToDoHelper() 7 @State var taskName: String = "" 8 var body: some View { 9 List { 10 HStack { 11 TextField(taskName, text: $taskName) 12 Spacer() 13 Button(action: addTask) { 14 Image(systemName: "plus.circle") 15 } 16 } 17 18 ForEach(self.tasks, id: .self) { task in 19 Text(task.name ?? "") 20 } 21 } 22 } 23 24 func addTask() { 25 self.add(taskName: self.taskName) 26 taskName = "" 27 } 28 func save() { 29 do { 30 try self.context.save() 31 } catch { 32 print(error) 33 } 34 } 35 36 func add(taskName: String) { 37 let newTask = ToDo(context: self.context) 38 newTask.id = UUID() 39 newTask.name = taskName 40 newTask.createdAt = Date() 41 save() 42 } 43}

補足情報(FW/ツールのバージョンなど)

macOS 15.5
XCode 11.5
Swift 5.2.4

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

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

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

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

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

guest

回答2

0

ベストアンサー

同じところでハマってました。
私はContentViewから値を渡してクラスのイニシャライザを設定することで動きました。

swift

1 2class ToDoHelper { 3 let context:NSManagedObjectContext 4 init(context:NSManagedObjectContext) { 5 self.context = context 6 } 7 8 func save() { 9 do { 10 try self.context.save() 11 } catch { 12 print(error) 13 } 14 } 15

投稿2021/02/08 08:26

melonpocky

総合スコア7

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

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

0

ToDoHelperクラスの中に
@PublishedでtaskName等使用する変数の定義が必要ですね

投稿2020/11/01 06:51

yukari_39

総合スコア23

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

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

Potewo

2020/11/01 11:32

回答ありがとうございます。 回答を参考に頑張ってみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問