0

Hy folks, I work on a litte project for a time tracker and use Core Data for storing the values. Every timer Note has a seconds value stored that runs from inside of each timer view. I want to populate now these values to the parent view, but it's not working even when binding the note to the view. I know i have to populate the changes somehow... Can somebody help?

ContentView:

import SwiftUI

extension Int: Identifiable {
    public var id: Int { self }
}

struct ContentView: View {
    
    let coreDM: CoreDataManager
    @State private var noteTitle: String = ""
    @State private var notes: [Note] = [Note]() // That's the Core Data Model
    
    private func populateNotes() {
        notes = coreDM.getAllNotes()
    }
    
    var body: some View {

            VStack {
                
                if notes.count > 0 {
                    ForEach(0..<$notes.count,id: \.self) { i in
                        Text("\(notes[i].seconds)")
                    }
                }
                
                TextField("Enter title", text: $noteTitle)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                
                Button("Save") {
                    coreDM.saveNote(title: noteTitle, seconds: 0)
                    populateNotes()
                }
                
                List {
                    
                    if notes.count > 0 {
                        
                        ForEach(0..<$notes.count,id: \.self) { i in
                            
                            NoteListView(note: $notes[i], coreDM: coreDM)
      
                            Button("Delete"){
                                coreDM.deleteNote(note: notes[i])
                                populateNotes()
                                
                            }
                        }
                    }
            
                }.listStyle(PlainListStyle())
                
                Spacer()
            }.padding()
            
            .onAppear(perform: {
                populateNotes()
            })

    }
}

NoteListView:

import SwiftUI
import Combine

struct NoteListView: View {
    
    @Binding var  note: Note
    let coreDM: CoreDataManager

    @State private var noteSeconds: Double = 0.0
    @State private var noteIsRunning: Bool = false
    
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        
        VStack{
        
            Text(note.title ?? "")

            Text("\(noteSeconds)")

                .onReceive(timer) { time in
                    if noteIsRunning {
                        noteSeconds += 1
                        note.seconds = noteSeconds
                    }
                }
            
            if noteIsRunning {
                Image(systemName: "pause.circle")
                    .resizable()
                    .frame(width:20, height: 20)
                    .onTapGesture {
                        withAnimation{
                            noteIsRunning.toggle()
                            note.seconds = noteSeconds
                            coreDM.updateNote()
                        }
                    }
                
            }else{
                Image(systemName: "record.circle")
                    .resizable()
                    .foregroundColor(.orange)
                    .frame(width:20, height: 20)
                    .onTapGesture {
                        withAnimation{
                            noteIsRunning.toggle()
                        }
                    }
            }
        }
        .onAppear(){
            noteSeconds = note.seconds
        }
        
    }
        
}

CoreDataManager:

import Foundation
import CoreData

class CoreDataManager {
    
    let persistentContainer: NSPersistentContainer
    
    init() {
        persistentContainer = NSPersistentContainer(name: "TimeTrackerDataModel2")
        persistentContainer.loadPersistentStores { (description, error) in
            if let error = error {
                fatalError("Core Data Store failed \(error.localizedDescription)")
            }
        }
    }
    
    func updateNote() {
        
        do {
            try persistentContainer.viewContext.save()
        } catch {
            persistentContainer.viewContext.rollback()
        }
        
    }
    
    func deleteNote(note: Note) {
        
        persistentContainer.viewContext.delete(note)
        
        do {
            try persistentContainer.viewContext.save()
        } catch {
            persistentContainer.viewContext.rollback()
            print("Failed to save context \(error)")
        }
    }
    
    func getAllNotes() -> [Note] {
        let fetchRequest: NSFetchRequest<Note> = Note.fetchRequest()
        
        do {
            return try persistentContainer.viewContext.fetch(fetchRequest)
        } catch {
            return []
        }
    }
    
    func saveNote(title: String, seconds: Double) {
        
        let note = Note(context: persistentContainer.viewContext)
        note.title = title
        note.seconds = seconds
        
        do {
            try persistentContainer.viewContext.save()
        } catch {
            print("Failed to save note: \(error)")
        }
        
    }
    
}
  • The `ForEach` binding should not be used with the indices use it with the object itself. But also CoreData objects are `ObservableObject` they need to be wrapped in `@ObservedObject` to see changes properly. They don't work well with `@State` and `@Binding` – lorem ipsum Jan 11 '22 at 14:42
  • Thanks @loremipsum! Could you give me code wise a hint how to achieve that? Still learning :) – Eddy Salzmann Jan 11 '22 at 16:12
  • [This](https://stackoverflow.com/questions/70016175/foreach-not-properly-updating-with-dynamic-content-swiftui/70130763#70130763) sample is a bit over complicated but it has the general idea of what you seem to be trying to do. Just the CoreData part not the timer part. – lorem ipsum Jan 11 '22 at 16:19

0 Answers0