SwiftUI - переменная self.fetchRequest, используемая перед инициализацией
Я пытаюсь создать динамические фильтры элементов Core Data с помощью SwiftUI. Основной объект данных: Item. Атрибуты: дата (Date), done (Boolean), name (String).
Предоставленный код создает 3 экземпляра объекта Item. Над списком есть сегментированный элемент управления для изменения значения фильтра. Все отключает фильтр (для doneFilter установлено значение nil). Не завершено включает фильтр (устанавливает для doneFilter значение false).
Также существует инициализация, которая устанавливает fetchRequest на основе сегментированного управления.
ПРОБЛЕМА
Сбой сборки с ошибкой:
Переменная self.fetchRequest использовалась перед инициализацией
Что не так с моим кодом?
ContentView.swift
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
var body: some View {
NavigationView {
VStack {
ListView()
}
.navigationBarTitle("Items")
.navigationBarItems(
leading:
Button(action: {
for number in 1...3 {
let item = Item(context: self.moc)
item.date = Date()
item.name = "Item \(number)"
item.done = false
do {
try self.moc.save()
}catch{
print(error)
}
}
}) {
Text("Add 3 items")
}
)
}
}
}
ListView.swift
import SwiftUI
struct ListView: View {
@Environment(\.managedObjectContext) var moc
var fetchRequest: FetchRequest<Item>
var items: FetchedResults<Item> { fetchRequest.wrappedValue }
@State var doneFilter :Bool? = nil
var doneStatus: Binding<Int> { Binding<Int>(
get: {
if self.doneFilter == false {
return 1
} else {
return 0
}
},
set: {
switch $0 {
case 1:
self.doneFilter = false
default:
self.doneFilter = nil
}
})
}
var body: some View {
List {
Picker(selection: doneStatus, label: Text("Picker")) {
Text("All").tag(0)
Text("Not finished").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
ForEach(items, id: \.self) {item in
HStack {
Text("\(item.name ?? "default item name")")
Spacer()
Toggle(isOn: Binding<Bool>(
get: { item.done },
set: {
item.done = $0
try? self.moc.save()
})) {
Text("Done")
}
.labelsHidden()
}
}
.onDelete(perform: removeItem)
}
}
func removeItem(at offsets: IndexSet) {
for offset in offsets {
let item = items[offset]
moc.delete(item)
}
try? moc.save()
}
init() {
if let filter = doneFilter {
fetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Item.name, ascending: true)
], predicate: NSPredicate(format: "done = %d", filter))
} else {
fetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Item.name, ascending: true)
])
}
}
}
1 ответ
Вот аналогичный код, который работает. Основное отличие состоит в том, что сегментированный элемент управления перемещается в ContentView и передает doneFilter в ListView через параметры в ListView(filter: doneFilter). Также есть некоторые изменения в коде инициализации в ListView.
ContentView.swift
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State var doneFilter :Bool? = nil
var doneStatus: Binding<Int> { Binding<Int>(
get: {
if self.doneFilter == false {
return 1
} else {
return 0
}
},
set: {
switch $0 {
case 1:
self.doneFilter = false
default:
self.doneFilter = nil
}
})
}
var body: some View {
NavigationView {
List {
Picker(selection: doneStatus, label: Text("Picker")) {
Text("All").tag(0)
Text("Not finished").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
ListView(filter: doneFilter)
}
.navigationBarTitle("Items")
.navigationBarItems(
leading:
Button(action: {
for number in 1...3 {
let item = Item(context: self.moc)
item.date = Date()
item.name = "Item \(number)"
item.done = false
do {
try self.moc.save()
}catch{
print(error)
}
}
}) {
Text("Add 3 items")
}
)
}
}
}
ListView.swift
import SwiftUI
struct ListView: View {
@Environment(\.managedObjectContext) var moc
var fetchRequest: FetchRequest<Item>
var items: FetchedResults<Item> { fetchRequest.wrappedValue }
var body: some View {
ForEach(items, id: \.self) {item in
HStack {
Text("\(item.name ?? "default item name")")
Spacer()
Toggle(isOn: Binding<Bool>(
get: { item.done },
set: {
item.done = $0
try? self.moc.save()
})) {
Text("Done")
}
.labelsHidden()
}
}
.onDelete(perform: removeItem)
}
func removeItem(at offsets: IndexSet) {
for offset in offsets {
let item = items[offset]
moc.delete(item)
}
try? moc.save()
}
init(filter: Bool?) {
if let filter = filter {
fetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Item.name, ascending: true)
], predicate: NSPredicate(format: "done = %d", filter))
} else {
fetchRequest = FetchRequest<Item>(entity: Item.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Item.name, ascending: true)
])
}
}
}
Но я до сих пор не знаю, как заставить работать первую версию. Один сегментированный элемент управления и инициализация обоих в ListView.