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 so forth & a stopwatch possibility.
I seen a bug that when the stopwatch is turned on and is counting high quality, 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 making an attempt to avoid wasting any information 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 problem, the database or certainly the counter
In my app there may be additionally primary counter that doesn’t save to the database. I added the timer view to examine 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 information 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
@Atmosphere(.modelContext) personal var context
@State personal var timeElapsed: TimeInterval = 0
@State personal var isRunning = false
personal let timer = Timer.publish(each: 1, on: .important, 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(measurement: 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 {
@Atmosphere(.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(measurement: 50))
.foregroundStyle(.darkGreyText)
.body(width: 95, peak: 50)
}
if !isBasic {
if isSecondCounter {
Textual content("(singleCounter2)")
.font(.system(measurement: 50))
.foregroundStyle(.darkGreyText)
.body(width: 95, peak: 50)
} else {
Textual content("(singleCounter1)")
.font(.system(measurement: 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 information being held for depend on tasks and examine consumer 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
}
// Kind beginning quantity
if settings.startNumberOfCounter != startNumber {
// Verify if the unique depend 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 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 subsequent iteration
startNumber = settings.startNumberOfCounter
}
// Verify if the unique depend 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 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 subsequent 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()
}
}```