I try to pass a fetched object User
to my detail-view, when user is selected in sidebar-view. I don't understand the error message:
Cannot convert return expression of type 'FetchedResults<User>.Element?' (aka 'Optional<User>') to return type 'Binding<User>'
.
my ContentView:
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(sortDescriptors: [SortDescriptor(\.name)])
private var users: FetchedResults<User>
@State private var selectedUserID: User.ID?
@State private var defaultUserID: User.ID?
var body: some View {
NavigationSplitView {
SidebarView(selection: selection)
} detail: {
if selection.wrappedValue != nil {
SecretDetailView(user: selectedUser) //<-- passing user object to sub-view
} else {
Text("Please select a user")
}
}
}
private var selection: Binding<User.ID?> {
Binding(get: { selectedUserID ?? defaultUserID }, set: { selectedUserID = $0 })
}
private var selectedUser: Binding<User> {
users.filter({ $0.id == selection.wrappedValue }).first //<-- ERROR!
}
}
my detail-View with user-Binding:
struct SecretDetailView: View {
@Binding var user: User //<-- user Binding in sub-view
var secretItems: [SecretItem] {
return user.secretItems
.filter {
searchText.isEmpty ? true : $0.userName.localizedCaseInsensitiveContains(searchText)
}
.sorted(using: sortOrder)
}
@State var searchText: String = ""
@State private var selection = Set<SecretItem.ID>()
@State var sortOrder: [KeyPathComparator<SecretItem>] = [
.init(\.userName, order: SortOrder.forward)
]
var table: some View {
Table(selection: $selection, sortOrder: $sortOrder) {
TableColumn("User Name", value: \.userName)
TableColumn("Password", value: \.password) { secretItem in
Text(secretItem.password)
}
} rows: {
ForEach(secretItems) { secretItem in
TableRow(secretItem)
}
}
}
var body: some View {
ZStack(alignment: .bottom) {
table
.searchable(text: $searchText)
.navigationTitle(user.name!)
....
Binding
to a FetchRequest
/NSManagedObject
is a fallacy.
The FetchRequest
is read-only and Binding
is for value types.
The only way to change a FetchRequest
is by affecting the store directly.
What you need is an
@ObservedObject var user: User
In the detail view, to edit and see changes of an NSManagedObject
You can use
@State private var selectedUser:User?
But you won’t be able to observe changes to the properties of the object just changed to selectedUser
as a whole.
Anytime you are creating a Binding
without a working get
and set
you should assume that there is a better approach. Binding
is by definition a two way connection.