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 undertaking pages are counters, notepad and many others & a stopwatch choice.
I seen a bug that when the stopwatch is turned on and is counting high quality, if the person is to work together with say the counter, then the stopwatch hangs for a second then carries on.
I’ve tried totally 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 fundamental counter that doesn’t save to the database. I added the timer view to verify if the identical bug occurs and it doesn’t. The stopwatch retains counting while the person 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 information?
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: .principal, in: .widespread).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, peak: 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(dimension: 24))
.foregroundStyle(.darkGreyText)
.body(width: 100, peak: 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 undertaking
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 undertaking
singleProject?.timer = timeElapsed
attempt? context.save()
}
}
})
}
// MARK: - Features
/// 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, peak: 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(dimension: 50))
.foregroundStyle(.darkGreyText)
.body(width: 95, peak: 50)
}
if !isBasic {
if isSecondCounter {
Textual content("(singleCounter2)")
.font(.system(dimension: 50))
.foregroundStyle(.darkGreyText)
.body(width: 95, peak: 50)
} else {
Textual content("(singleCounter1)")
.font(.system(dimension: 50))
.foregroundStyle(.darkGreyText)
.body(width: 95, peak: 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, peak: 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 verify person settings for adjustments
// Verify for a undertaking 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 previous 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 person 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 subsequent iteration
startNumber = settings.startNumberOfCounter
}
// Verify if the unique rely matched the previous 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 person 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 subsequent iteration
startNumber = settings.startNumberOfCounter
}
}
} else {
// Change fundamental counter to new begin quantity
counterBasic = settings.startNumberOfCounter
}
}
}
/// Save counters to the database relying on what counters are being utilized by person
func saveCounter(counter1: Int, counter2: Int) {
if !isSecondCounter {
singleProject?.counter1 = counter1
} else if isSecondCounter {
singleProject?.counter2 = counter2
}
attempt? context.save()
}
}```