Friday, May 24, 2024
HomeIOS Developmentios - Stopwatch going into background when consumer interacts with completely different...

ios – Stopwatch going into background when consumer interacts with completely different aspect solely on pages utilizing SwiftData in SwiftUI


I’m constructing my first app retailer app, a knitting/crochet app.

In my app are tasks which can be saved to SwiftData. On these venture pages are counters, notepad and so on & a stopwatch possibility.

I observed a bug that when the stopwatch is turned on and is counting wonderful, if the consumer is to work together with say the counter, then the stopwatch hangs for a second then carries on.

I’ve tried completely different choices with attempting to save lots of any knowledge on a background thread, however that simply made the app sluggish and un-usable. I’m on the level of a lot confusion whether or not its the timer that has a difficulty, the database or certainly the counter

In my app there’s additionally primary counter that doesn’t save to the database. I added the timer view to test if the identical bug occurs and it doesn’t. The stopwatch retains counting while the consumer interacts with the counter no points which makes me suspect that its one thing to do with the counter & the stopwatch knowledge being saved.

I’ve additionally tried a stopwatch class utilizing Date() as a substitute of Timer however that did the very same factor…am I going mad or is that this a factor with the info?

Right here is my Timer

import SwiftUI
import SwiftData


struct TimerView: View {
    
    var projectPart: MultiProjectParts?
    var singleProject: SingleProject?
    
    @State var isSingleProject: Bool
    @Surroundings(.modelContext) personal var context
    
    @State personal var timeElapsed: TimeInterval = 0
    @State personal var isRunning = false
    
    personal let timer = Timer.publish(each: 1, on: .major, in: .frequent).autoconnect()

    
    var physique: some View {
        
        @State var updatedMultiTimer = projectPart?.timer
        @State var updatedSingleTimer = singleProject?.timer
        
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(.white)
                .shadow(radius: 5)
                .body(width: 257, top: 100)
            
            HStack(spacing: 36) {
                // Cease
                Button {
                   cease()
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .body(width: 37)
                        
                        Picture(systemName: Constants.timerStop)
                            .resizable()
                            .aspectRatio(contentMode: .match)
                            .body(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                .padding(.main, 10)
                // Counter
                VStack (spacing:10) {
                    
                    Textual content(isSingleProject ? timeFormatted(time: updatedSingleTimer!) : timeFormatted(time: updatedMultiTimer!))
                        .font(.system(measurement: 24))
                        .foregroundStyle(.darkGreyText)
                        .body(width: 100, top: 50)
                }
                
                // Play/pause
                Button {
                    if !isRunning {
                       begin()
                    }
                    
                    else if isRunning {
                        pause()
                    }
                   
                    
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .body(width: 37)
                        
                        Picture(systemName: isRunning ? Constants.timerPause : Constants.timerPlay)
                            .resizable()
                            .aspectRatio(contentMode: .match)
                            .body(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                .padding(.trailing, 10)
                
            }
        }
        .onAppear {
            if projectPart?.timer ?? 0.0 > 0 {
                // If there's a reminiscence of a timer, load the reminiscence on seem
                timeElapsed = projectPart!.timer
            }
            if singleProject?.timer ?? 0.0 > 0 {
                timeElapsed = singleProject!.timer
            }
            
        }
        .onReceive(timer, carry out: { _ in
            if self.isRunning {
                if !isSingleProject {
                    
                self.timeElapsed += 1
                // Save to multi mannequin
                projectPart?.timer = timeElapsed
                attempt? context.save()
                    
                } else {
                    // Is a single venture
                    self.timeElapsed += 1
                    singleProject?.timer = timeElapsed
                    // Save to single mannequin
                    attempt? context.save()
                }
                
                
            } else if !self.isRunning && timeElapsed == 0 {
                if !isSingleProject {
                    // Save to multi mannequin
                    projectPart?.timer = timeElapsed
                    attempt? context.save()
                } else {
                    // Is a single venture
                    singleProject?.timer = timeElapsed
                    attempt? context.save()
                }
            }
        })
    }
    
// MARK: - Capabilities
    /// Begin the timer
   personal func begin() {
       isRunning = true
    }
    
    /// Pause the timer
    personal func pause() {
        isRunning = false
    }
    
    /// Cease & reset the timer
   personal  func cease() {
       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)
    }
    
}

Right here is my counter view

import SwiftUI

struct CounterViewSingle: View {
    
    @Surroundings(.modelContext) personal var context
    
    @State var isSecondCounter: Bool
    
    @ObservedObject var settings = SettingsData()
    
    var singleProject: SingleProject?
    
    var isBasic: Bool
    
    @State personal var counterBasic = 00
    @State personal var singleCounter1 = 0
    @State personal var singleCounter2 = 0
    
    @State personal var startNumber = 0
    
    var physique: some View {
        
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(.white)
                .shadow(radius: 5)
                .body(width: 257, top: 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 mannequin
                    saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .body(width: 37)
                        
                        Picture(systemName: Constants.counterMinus)
                            .resizable()
                            .aspectRatio(contentMode: .match)
                            .body(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
                
                // Counter & Reset
                VStack (spacing:10) {
                    if isBasic {
                        Textual content("(counterBasic)")
                            .font(.system(measurement: 50))
                            .foregroundStyle(.darkGreyText)
                            .body(width: 95, top: 50)
                    }
                    if !isBasic {
                        if isSecondCounter {
                            Textual content("(singleCounter2)")
                                .font(.system(measurement: 50))
                                .foregroundStyle(.darkGreyText)
                                .body(width: 95, top: 50)
                        } else {
                            Textual content("(singleCounter1)")
                                .font(.system(measurement: 50))
                                .foregroundStyle(.darkGreyText)
                                .body(width: 95, top: 50)
                        }
                    }
                    
                    // Reset
                    Button {
                        if isBasic {
                            counterBasic = settings.startNumberOfCounter
                        }
                        if !isBasic {
                            if !isSecondCounter {
                                singleCounter1 = startNumber
                            } else if isSecondCounter {
                                singleCounter2 = startNumber
                            }
                        }
                        // Save to mannequin
                        saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                        
                    } label: {
                        ZStack {
                            RoundedRectangle(cornerRadius: 15)
                                .fill(.sunflower)
                                .body(width: 90, top: 18)
                            Textual content(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 mannequin
                    saveCounter(counter1: singleCounter1, counter2: singleCounter2)
                } label: {
                    ZStack {
                        Circle()
                            .fill(.sunflower)
                            .body(width: 37)
                        
                        Picture(systemName: Constants.counterAdd)
                            .resizable()
                            .aspectRatio(contentMode: .match)
                            .body(width: 30)
                            .foregroundStyle(.white)
                        
                    }
                }
            }
        }
        .onAppear {
            /// Verify for knowledge being held for rely on tasks and test consumer settings for modifications
            // Verify for a venture first
            if !isBasic {
                
                // Load any reminiscence of counters
                if singleProject?.counter1 ?? 0 > 0 {
                    singleCounter1 = singleProject!.counter1
                }
                
                if singleProject?.counter2 ?? 0 > 0 {
                    singleCounter2 = singleProject!.counter2
                }
                
                // Type beginning quantity
                if settings.startNumberOfCounter != startNumber {
                    // Verify if the unique rely matched the outdated begin quantity to then be reset for counter 1
                    if singleCounter1 == startNumber {
                        // Set the brand new baseline quantity
                        singleCounter1 = settings.startNumberOfCounter
                        // Set the brand new begin quantity
                        startNumber = settings.startNumberOfCounter
                    } 
                    // The baseline is already decrease than the consumer has set
                    else if singleCounter1 < settings.startNumberOfCounter {
                        // Set the brand new baseline quantity
                        singleCounter1 = settings.startNumberOfCounter
                        // Set the brand new begin quantity
                        startNumber = settings.startNumberOfCounter
                    }
                    else {
                        // Counter already in use, don't reset to new quantity however change the beginning quantity for the following iteration
                        startNumber = settings.startNumberOfCounter
                    }
                    
                    // Verify if the unique rely matched the outdated begin quantity to then be reset for counter 2
                    if singleCounter2 == startNumber {
                        // Set the brand new baseline quantity
                        singleCounter2 = settings.startNumberOfCounter
                        // Set the brand new begin quantity
                        startNumber = settings.startNumberOfCounter
                    } 
                    // The baseline is already decrease than the consumer has set
                    else if singleCounter2 < settings.startNumberOfCounter {
                        // Set the brand new baseline quantity
                        singleCounter2 = settings.startNumberOfCounter
                        // Set the brand new begin quantity
                        startNumber = settings.startNumberOfCounter
                    }
                    else {
                        // Counter already in use, don't reset to new quantity however change the beginning quantity for the following iteration
                        startNumber = settings.startNumberOfCounter
                    }
                }
            } else {
                // Change primary counter to new begin quantity
                counterBasic = settings.startNumberOfCounter
            }
        }
    }
    /// Save counters to the database relying on what counters are being utilized by consumer
    func saveCounter(counter1: Int, counter2: Int) {
        if !isSecondCounter {
            singleProject?.counter1 = counter1
        } else if isSecondCounter {
            singleProject?.counter2 = counter2
        }
        
        attempt? context.save()
    }
}```

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments