Hello,
I'm using a NavigationSplitView and feeding the sidebar using FetchResults. In the detail view, the user can update the field name of the entity and then, the sidebar becomes useless, selecting random rows and hiding some of them, like these pictures:
I could reproduce the issue using a sample project. If you want to have the project to test it, create a simple project on Xcode: iOS app / +Use Core Data & +Host in CloudKit
Then, add a field "name String?" in the Entity and use this code in ContentView.swift:
import SwiftUI
import CoreData
extension Binding {
public func replaceNil<T>(_ defaultValue: T) -> Binding<T> where Value == Optional<T> {
return .init {
wrappedValue ?? defaultValue
} set: {
wrappedValue = $0
}
}
}
struct Header: View {
@ObservedObject var item: Item
var body: some View {
VStack {
if let name = item.name, !name.isEmpty {
Text(name)
} else {
Text("---")
.foregroundColor(.secondary)
}
}
}
}
struct ItemDetail: View {
@ObservedObject var item: Item
var body: some View {
VStack {
Spacer()
if let timestamp = item.timestamp {
Text(timestamp, formatter: itemFormatter)
} else {
Text("---")
.foregroundColor(.secondary)
}
Button("Update") {
item.timestamp = Date.now
}
TextField("Name", text: $item.name.replaceNil(""))
Spacer()
}
.padding()
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var selected: Item.ID?
@FetchRequest(
sortDescriptors: [
SortDescriptor(\.name, order: .forward),
SortDescriptor(\.timestamp, order: .forward),
],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationSplitView {
List(selection: $selected) {
ForEach(items) { item in
Header(item: item)
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
} detail: {
if let selected, let index = items.firstIndex(where: { $0.id == selected }) {
ItemDetail(item: items[index])
} else {
Text("Select something")
}
}
}
private func addItem() {
withAnimation {
do {
let newItem = Item(context: viewContext)
newItem.name = "Name"
newItem.timestamp = Date()
try viewContext.save()
selected = newItem.id
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
.previewInterfaceOrientation(.landscapeLeft)
}
}
Any ideas?