タイトル長くなってます(汗
CoreDataのデータを@FetchRequestで取得して、その要素を別ビューに編集用に渡したい時ありますよね。
その時に、どうやって渡せば良いかのメモです。
MEMO
CoreDataでは以下のようなモデルを作り、適当なWrappedPropertyを作っています。
- Recordは、CoreDataのEntity
- volumeは、Recordのプロパティ
前提のContentView
以下のContentViewがあって、DetailViewという子ViewにRecordを渡して、詳細を編集できるようにします。
問題は、DetailViewにどうやって要素を渡すのか? という点です。
struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(entity: Record.entity(), sortDescriptors: [ NSSortDescriptor(keyPath: \Record.date, ascending: false) ]) var records: FetchedResultsvar 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)で、以下のように定義すると思います。
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)では、以下のように渡すと思うのですが、エラーになります。
DetailView(record: self.$records[index])
NSManagedObjectは、ObservableObjectでした
NSManagedObjectは、ObservableObjectで、各プロパティは、@Published相当らしいです。
@Observedで受けようとして受け側のビュー(DetailView)で、以下のように定義してみました。
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)では、以下のように渡すと期待通りの動きになります。
DetailView(record: self.records[index])
テストしたコード
XCode11.4を使って、以下のコードで動作を確認しました。
ContentView.swift
struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(entity: Record.entity(), sortDescriptors: [ NSSortDescriptor(keyPath: \Record.date, ascending: false) ]) var records: FetchedResultsvar 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
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