Search code examples
swiftuicore-data

Unwrapping Optional Entity


Let's say that Item is a CoreData entity:

struct ItemDetailView: View {
@Binding var item: Item?
@Binding var isEditing: Bool

var body: some View{
    if isEditing {
        TextField( "Name", text: Binding($item.data)! )
    }else{
        Text(item!.data!)
    }
}
}

Error: Value of optional type 'Item?' must be unwrapped to refer to member 'data' of wrapped base type 'Item'


Edit All the code:

import SwiftUI
import CoreData

struct ItemDetailView: View {
    @Binding var item: Item?
    @Binding var isEditing: Bool
    
    var body: some View{
        if isEditing {
            
            TextField( "Name", text: Binding($item.data)! )
        }else{
            Text(item!.data!)
        }
    }
}

struct ItemEditorView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.dismiss) private var dismiss
    
    let isNew: Bool
    @State var isEditing: Bool = false
    @State var item: Item?
    @Binding var newItem: Item?
    
    
    var body: some View {
        
        if isNew {
                
        }
        
        NavigationView{
            ItemDetailView(item: isNew ? $newItem : $item, isEditing: $isEditing)
                .toolbar {
                    ToolbarItem {
                        Button(isNew ? "Add" : (isEditing ? "Done" : "Edit")) {
                            //TBI
                            if isNew {
                                
                            }
                        }
                    }
                    ToolbarItem(placement:.cancellationAction){
                        Button("Cancel"){
                            dismiss()
                        }
                    }
                }
                .navigationTitle("Item Editor")
        }
    }
}

struct ItemsListView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.data, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
    @State var presentNewItemEditorView = false
    @State var newItem: Item?
    
    var body: some View {
        NavigationView {
            VStack{
                Text(newItem?.data ?? "nil")
                List {
                    ForEach(items){ item in
                        NavigationLink(item.data!, destination: ItemEditorView(isNew: false, item:item, newItem: $newItem))
                    }
                }
            }
            .fullScreenCover(isPresented: $presentNewItemEditorView, content: {
                ItemEditorView(isNew: true, isEditing: true, newItem: $newItem)
            })
            .navigationTitle("Main")
            .toolbar {
                ToolbarItem {
                    Button("New goal"){
                        presentNewItemEditorView = true
                    }
                }
            }
            .task {
                newItem = Item(context: viewContext)
                newItem!.data = "New item text"
            }
        }
    }
}

Solution

  • Implementation by Conny Wals sugested by @vadian

        import CoreData
        class EditViewModel {
            /// **new** instance of item to edit. Don't edit the item, if any, that we pass to VM
            var item: Item4
            /// Context to use in the isolated changes
            let context: NSManagedObjectContext
            
            /// Pass an item if  you want to edit one, or nil to generate one
            init(item: Item4? = nil){
                self.context = PersistenceController.shared.childViewContext()
                if let item = item {
                    self.item = PersistenceController.shared.copyForEditing(of: item, in: context)
                } else {
                    self.item = PersistenceController.shared.newTemporaryInstance(in: context)
                }
            }
            
            func persist(){
                PersistenceController.shared.persist(item)
            }
        }
    
        import SwiftUI
    
        struct EditItemView: View {
            @State var viewModel: EditViewModel
            @Environment(\.dismiss) private var dismiss
            var body: some View {
                NavigationView {
                    VStack{
                        
                        TextField( "Item Name", text: Binding(get: {viewModel.item.name ?? ""}, set: {viewModel.item.name = $0}))
                        
                        
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                        Spacer()
                    }
                    .navigationTitle("Edit Item")
                    .navigationBarItems(leading: Button("Cancel"){
                        dismiss()
                    }, trailing: Button("Save"){
                        viewModel.persist()
                        dismiss()
                    })
                }
            }
        }
    
        struct ItemsView04: View {
            @FetchRequest(
                sortDescriptors: [NSSortDescriptor(keyPath: \Item3.name, ascending: true)],
                animation: .default)
            private var items: FetchedResults<Item4>
            
            @State var editItemViewIsPresent = false
            
            var body: some View {
                NavigationView{
                    List {
                        ForEach(items){ item in
                            NavigationLink(item.name ?? ""){
                                EditItemView(viewModel: EditViewModel(item: item))
                            }
                        }
                    }
                    .toolbar {
                        ToolbarItem {
                            Button {
                                editItemViewIsPresent = true
                            }label:{
                                Text("+")
                            }
                        }
                    }
                    .fullScreenCover(isPresented: $editItemViewIsPresent){
                        EditItemView(viewModel: EditViewModel())
                    }
                }
            }
        }