Brenton Beltrami
Brenton Beltrami

Reputation: 413

TextField with CoreData

Goal: To have unlimited TextFields in list with CRUD functionality using coredata.

Example of goal but w/o Coredata:

struct Item: Identifiable {
    let id = UUID()
    var title: String
}

class TestItems: ObservableObject {
    @Published var items = [Item]()
}


struct ContentView: View {
    @ObservedObject var itemGroup = TestItems()
    
    
    var body: some View {
        NavigationView{
            
            List{
                ForEach(itemGroup.items.indices, id:\.self) { index in
                    TextField("Type Stuff Here", text: $itemGroup.items[index].title)
                }
                .onDelete(perform: removeRows)
                
            }
            .navigationBarTitle("Working Example")
            .navigationBarItems(trailing: Button(action: {
                let stuff = Item(title: "")
                itemGroup.items.append(stuff)
            }, label: {
                Text("Add")
            }))
            
        }
        
    }
    
    func removeRows(at offsets: IndexSet) {
        itemGroup.items.remove(atOffsets: offsets)
    }
}

My Attempt with Coredata:

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Item.timestamp, ascending: true),
            NSSortDescriptor(keyPath: \Item.title, ascending: true)
        ],
        animation: .default)
    private var items: FetchedResults<Item>
    
    
    var body: some View {
        NavigationView{
            List {
                ForEach(items) { item in
                    TextField("Type Response Here", text: $item.title) //<-- This returns an error "cannot find item in scope"
                }
                .onDelete(perform: deleteItems)
            }
            .navigationBarTitle("CoreData")
            .navigationBarItems(trailing: Button(action: {
                addItem()
            }, label: {
                Text("Add Item")
            }))
        }
    }
    
    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
            newItem.title = "Hello"
            
            do {
                try viewContext.save()
            } 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)")
            }
        }
    }
}

I have also attempted to use structs & classes with Coredata (like my example) but am unable to find a way to save a class into Coredata if that is even possible.

Upvotes: 2

Views: 300

Answers (1)

Asperi
Asperi

Reputation: 258117

You need to use ObservedObject over CoreData object and for this it is better to created separated sub-view for row, like

ForEach(items) { item in
    ItemView(item: item)
}

and ItemView

struct ItemView: View {
   @ObservedObject var item: Item

   var body: some View {
      // now binding over item title is provided by ObservedObject wrapper
      TextField("Type Response Here", text: $item.title)
   }
}

Update: handling of optional properties might differ depending on which behavior is expected. Here is possible variant:

   var body: some View {
      let text = Binding(
         get: { item.title ?? "" },
         set: { item.title = $0 }
      )
      TextField("Type Response Here", text: text)
   }

Note: entering text into field does not save CoreData object, so you need to think where to save it, possible variant is in .onCommit for TextField.

Upvotes: 2

Related Questions