タイトル長くなってます(汗
CoreDataのデータを@FetchRequestで取得して、その要素を別ビューに編集用に渡したい時ありますよね。
その時に、どうやって渡せば良いかのメモです。
MEMO
CoreDataでは以下のようなモデルを作り、適当なWrappedPropertyを作っています。
- Recordは、CoreDataのEntity
- volumeは、Recordのプロパティ
前提のContentView
以下のContentViewがあって、DetailViewという子ViewにRecordを渡して、詳細を編集できるようにします。
問題は、DetailViewにどうやって要素を渡すのか? という点です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(entity: Record.entity(), sortDescriptors: [ NSSortDescriptor(keyPath: \Record.date, ascending: false) ]) var records: FetchedResults<Record> var body: some View { NavigationView { VStack { List { ForEach(records.indices, id: \.self) { index in NavigationLink(destination: DetailView(record: ????), label: { Text("\(self.records[index].formattedDate) \(self.records[index].formattedVolume) ") }) } } Button(action: { self.addSample() }, label: { Text("Add Sample") }) } .navigationBarTitle("Water Record") } } func addSample() { let today = DateInRegion(Date(), region: Region.current).dateAtStartOf(.day) let record8am = Record.init(context: self.moc) record8am.date = today.dateByAdding(8, .hour).date record8am.volume = 30 let record12pm = Record.init(context: self.moc) record12pm.date = today.dateByAdding(12, .hour).date record12pm.volume = 50 let record7pm = Record.init(context: self.moc) record7pm.date = today.dateByAdding(19, .hour).date record7pm.volume = 90 try? self.moc.save() } } |
@FetchRequestの要素はBindingで渡せない
@Bindingで受けようとすると、受け側のビュー(DetailView)で、以下のように定義すると思います。
1 2 3 4 5 6 7 8 9 10 11 |
struct DetailView: View { @Environment(\.managedObjectContext) var moc @Binding var record:Record var body: some View { return VStack { Text("\(record.formattedDate)") Text("Volume: \(record.formattedVolume)") Slider(value: $record.volume, in: 0...100, step: 5) } } } |
渡す側(ContentView)では、以下のように渡すと思うのですが、エラーになります。
1 |
DetailView(record: self.$records[index]) |
NSManagedObjectは、ObservableObjectでした
NSManagedObjectは、ObservableObjectで、各プロパティは、@Published相当らしいです。
@Observedで受けようとして受け側のビュー(DetailView)で、以下のように定義してみました。
1 2 3 4 5 6 7 8 9 10 11 12 |
struct DetailView: View { @Environment(\.managedObjectContext) var moc @ObservedObject var record:Record // BindingではなくObservedObject var body: some View { return VStack { Text("\(record.formattedDate)") Text("Volume: \(record.formattedVolume)") Slider(value: $record.volume, in: 0...100, step: 5) } } } |
渡す側(ContentView)では、以下のように渡すと期待通りの動きになります。
1 |
DetailView(record: self.records[index]) |
テストしたコード
XCode11.4を使って、以下のコードで動作を確認しました。
ContentView.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(entity: Record.entity(), sortDescriptors: [ NSSortDescriptor(keyPath: \Record.date, ascending: false) ]) var records: FetchedResults<Record> var body: some View { NavigationView { VStack { List { ForEach(records.indices, id: \.self) { index in NavigationLink(destination: DetailView(record: self.records[index]), label: { Text("\(self.records[index].formattedDate) \(self.records[index].formattedVolume) ") }) } } Button(action: { self.addSample() }, label: { Text("Add Sample") }) } .navigationBarTitle("Water Record") } } func addSample() { let today = DateInRegion(Date(), region: Region.current).dateAtStartOf(.day) let record8am = Record.init(context: self.moc) record8am.date = today.dateByAdding(8, .hour).date record8am.volume = 30 let record12pm = Record.init(context: self.moc) record12pm.date = today.dateByAdding(12, .hour).date record12pm.volume = 50 let record7pm = Record.init(context: self.moc) record7pm.date = today.dateByAdding(19, .hour).date record7pm.volume = 90 try? self.moc.save() } } |
DetailView.swift
1 2 3 4 5 6 7 8 9 10 11 12 |
struct DetailView: View { @Environment(\.managedObjectContext) var moc @ObservedObject var record:Record var body: some View { return VStack { Text("\(record.formattedDate)") Text("Volume: \(record.formattedVolume)") Slider(value: $record.volume, in: 0...100, step: 5) } } } |
Sponsor Link