Stopwatch going into background when user interacts with different element only on pages using SwiftData

Hi everyone. I am building my first app store app, a knitting/crochet app.

In my app are projects that are saved to SwiftData. On those project pages are counters, notepad etc & a stopwatch option.

I noticed a bug that when the stopwatch is turned on and is counting fine, if the user is to interact with say the counter, then the stopwatch hangs for a moment then carries on.

I have tried different options with trying to save any data on a background thread, but that just made the app sluggish and un-usable. I am at the point of so much confusion whether its the timer that has an issue, the database or indeed the counter

In my app there is also basic counter that doesn’t save to the database. I added the timer view to check if the same bug happens and it doesn’t. The stopwatch keeps counting whilst the user interacts with the counter no issues which makes me suspect that its something to do with the counter & the stopwatch data being saved.

I have also tried a stopwatch class using Date() instead of Timer but that did the exact same thing…am I going mad or is this a thing with the data?

Here is my Timer

import SwiftUI
import SwiftData


struct TimerView: View {
    
    var projectPart: MultiProjectParts?
    var singleProject: SingleProject?
    
    @State var isSingleProject: Bool
    @Environment(\.modelContext) private var context
    
    @State private var timeElapsed: TimeInterval = 0
    @State private var isRunning = false
    
    private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    
    var body: some View {
        
        @State var updatedMultiTimer = projectPart?.timer
        @State var updatedSingleTimer = singleProject?.timer
        
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(.white)
                .shadow(radius: 5)
                .frame(width: 257, height: 100)
            
            HStack(spacing: 36) {
                // Stop
                Button {
                   stop()
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .frame(width: 37)
                        
                        Image(systemName: Constants.timerStop)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                .padding(.leading, 10)
                // Counter
                VStack (spacing:10) {
                    
                    Text(isSingleProject ? timeFormatted(time: updatedSingleTimer!) : timeFormatted(time: updatedMultiTimer!))
                        .font(.system(size: 24))
                        .foregroundStyle(.darkGreyText)
                        .frame(width: 100, height: 50)
                }
                
                // Play/pause
                Button {
                    if !isRunning {
                       start()
                    }
                    
                    else if isRunning {
                        pause()
                    }
                   
                    
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .frame(width: 37)
                        
                        Image(systemName: isRunning ? Constants.timerPause : Constants.timerPlay)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                .padding(.trailing, 10)
                
            }
        }
        .onAppear {
            if projectPart?.timer ?? 0.0 > 0 {
                // If there is a memory of a timer, load the memory on appear
                timeElapsed = projectPart!.timer
            }
            if singleProject?.timer ?? 0.0 > 0 {
                timeElapsed = singleProject!.timer
            }
            
        }
        .onReceive(timer, perform: { _ in
            if self.isRunning {
                if !isSingleProject {
                    
                self.timeElapsed += 1
                // Save to multi model
                projectPart?.timer = timeElapsed
                try? context.save()
                    
                } else {
                    // Is a single project
                    self.timeElapsed += 1
                    singleProject?.timer = timeElapsed
                    // Save to single model
                    try? context.save()
                }
                
                
            } else if !self.isRunning && timeElapsed == 0 {
                if !isSingleProject {
                    // Save to multi model
                    projectPart?.timer = timeElapsed
                    try? context.save()
                } else {
                    // Is a single project
                    singleProject?.timer = timeElapsed
                    try? context.save()
                }
            }
        })
    }
    
// MARK: - Functions
    /// Start the timer
   private func start() {
       isRunning = true
    }
    
    /// Pause the timer
    private func pause() {
        isRunning = false
    }
    
    /// Stop & reset the timer
   private  func stop() {
       isRunning = false
       timeElapsed = 0
    }
    
    /// Format the timer for 00:00:00 string
    func timeFormatted(time: TimeInterval) -> String {
        let hours = Int(time) / 3600
        let minutes = Int(time.truncatingRemainder(dividingBy: 3600)) / 60
        let seconds = Int(time) % 60
        return String(format: Constants.timerFormat, hours, minutes, seconds)
    }
    
}```

Here is the counter view

import SwiftUI

struct CounterViewSingle: View {
    
    @Environment(\.modelContext) private var context
    
    @State var isSecondCounter: Bool
    
    @ObservedObject var settings = SettingsData()
    
    var singleProject: SingleProject?
    
    var isBasic: Bool
    
    @State private var counterBasic = 00
    @State private var singleCounter1 = 0
    @State private var singleCounter2 = 0
    
    @State private var startNumber = 0
    
    var body: some View {
        
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(.white)
                .shadow(radius: 5)
                .frame(width: 257, height: 100)
            
            HStack(spacing: 36) {
                // Minus
                Button {
                    if isBasic {
                        counterBasic -= settings.counterInterval
                    }
                    if !isBasic {
                        if !isSecondCounter {
                            singleCounter1 -= settings.counterInterval
                        } else if isSecondCounter {
                            singleCounter2 -= settings.counterInterval
                        }
                    }
                    // Save to model
                    saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .frame(width: 37)
                        
                        Image(systemName: Constants.counterMinus)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                
                // Counter & Reset
                VStack (spacing:10) {
                    if isBasic {
                        Text("\(counterBasic)")
                            .font(.system(size: 50))
                            .foregroundStyle(.darkGreyText)
                            .frame(width: 95, height: 50)
                    }
                    if !isBasic {
                        if isSecondCounter {
                            Text("\(singleCounter2)")
                                .font(.system(size: 50))
                                .foregroundStyle(.darkGreyText)
                                .frame(width: 95, height: 50)
                        } else {
                            Text("\(singleCounter1)")
                                .font(.system(size: 50))
                                .foregroundStyle(.darkGreyText)
                                .frame(width: 95, height: 50)
                        }
                    }
                    
                    // Reset
                    Button {
                        if isBasic {
                            counterBasic = settings.startNumberOfCounter
                        }
                        if !isBasic {
                            if !isSecondCounter {
                                singleCounter1 = startNumber
                            } else if isSecondCounter {
                                singleCounter2 = startNumber
                            }
                        }
                        // Save to model
                        saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                        
                    } label: {
                        ZStack {
                            RoundedRectangle(cornerRadius: 15)
                                .fill(.sunflower)
                                .frame(width: 90, height: 18)
                            Text(Constants.counterReset)
                                .font(.notesText)
                                .foregroundStyle(.darkGreyText)
                        }
                    }
                }
                
                // Plus
                Button {
                    if isBasic {
                        counterBasic += settings.counterInterval
                    }
                    if !isBasic {
                        if !isSecondCounter {
                            singleCounter1 += settings.counterInterval
                        } else if isSecondCounter {
                            singleCounter2 += settings.counterInterval
                        }
                    }
                    // Save to model
                    saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .frame(width: 37)
                        
                        Image(systemName: Constants.counterAdd)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
            }
        }
        .onAppear {
            /// Check for data being held for count on projects and check user settings for changes
            // Check for a project first
            if !isBasic {
                
                // Load any memory of counters
                if singleProject?.counter1 ?? 0 > 0 {
                    singleCounter1 = singleProject!.counter1
                }
                
                if singleProject?.counter2 ?? 0 > 0 {
                    singleCounter2 = singleProject!.counter2
                }
                
                // Sort starting number
                if settings.startNumberOfCounter != startNumber {
                    // Check if the original count matched the old start number to then be reset for counter 1
                    if singleCounter1 == startNumber {
                        // Set the new baseline number
                        singleCounter1 = settings.startNumberOfCounter
                        // Set the new start number
                        startNumber = settings.startNumberOfCounter
                    } 
                    // The baseline is already lower than the user has set
                    else if singleCounter1 < settings.startNumberOfCounter {
                        // Set the new baseline number
                        singleCounter1 = settings.startNumberOfCounter
                        // Set the new start number
                        startNumber = settings.startNumberOfCounter
                    }
                    else {
                        // Counter already in use, do not reset to new number but change the start number for the next iteration
                        startNumber = settings.startNumberOfCounter
                    }
                    
                    // Check if the original count matched the old start number to then be reset for counter 2
                    if singleCounter2 == startNumber {
                        // Set the new baseline number
                        singleCounter2 = settings.startNumberOfCounter
                        // Set the new start number
                        startNumber = settings.startNumberOfCounter
                    } 
                    // The baseline is already lower than the user has set
                    else if singleCounter2 < settings.startNumberOfCounter {
                        // Set the new baseline number
                        singleCounter2 = settings.startNumberOfCounter
                        // Set the new start number
                        startNumber = settings.startNumberOfCounter
                    }
                    else {
                        // Counter already in use, do not reset to new number but change the start number for the next iteration
                        startNumber = settings.startNumberOfCounter
                    }
                }
            } else {
                // Change basic counter to new start number
                counterBasic = settings.startNumberOfCounter
            }
        }
    }
    /// Save counters to the database depending on what counters are being used by user
    func saveCounter(counter1: Int, counter2: Int) {
        if !isSecondCounter {
            singleProject?.counter1 = counter1
        } else if isSecondCounter {
            singleProject?.counter2 = counter2
        }
        
        try? context.save()
    }
}


These views are just called in a project view etc so I don’t think the issue is there but if you need any additional code please say. Also the counter view is also using data from App Storage based on user settings such as starting number, count interval etc. Thanks in advance for any advice.