Maksym Kucherenko
Maksym Kucherenko

Reputation: 109

Any better way to change timer every 24 hours in the background - SwiftUI

I have a complex issue and if u make it clearer to me, I think it might help many lads. I am stuck due to impossibility to run a timer in the background (when app is closed). Moreover, I didn't find anything that could directly correspond to my situation.(The question is mentioned bellow)

Explanation

My watchOS app allow to make goals.It has two pickers where the user chooses how many weeks/months/days will be settled to the deadline. @State private var values = ["Weeks", "Months", "Years"] - that are the values of the second picker.

Picker(selection: $indexnum, label: Text("")) {
            ForEach(1...12, id:\.self) { indexnum in
              Text(String(indexnum))
           }
         }
      
       Picker(selection: $index, label: Text("")) {
        ForEach(0..<values.count, content: { index in
             Text(self.values[index])
          })
        }

After the user made a pick, I am saving everything to the Core Data. However, I give a condition to "convert" my weeks/months/years to the approximate number of days by multiplying on picker indexes:

private func addGoal(){
if index == 0 {
    pickedValueDateN = 7 * indexnum ///--> I will give an explanations why I need two values further
    pickedValueDateN1 = 7 * indexnum
    } else if index == 1{
        pickedValueDateN = 30 * indexnum
        pickedValueDateN1 = 30 * indexnum
    } else if index == 2{
        pickedValueDateN = 365 * indexnum
        pickedValueDateN1 = 365 * indexnum
} else {
    pickedValueDateS = "N/A"
    pickedValueDateN = 1
}
let goal = NewGoal(context: context)
goal.dateAdded = Date()
goal.pickedValueDateN = Int64(pickedValueDateN) ////ALL DAYS
goal.pickedValueDateN1 = Int64(pickedValueDateN1) ////MINUS DAYS
do{
    try context.save()
    presentationMode.wrappedValue.dismiss()
}catch let err{
    print(err.localizedDescription)
    }
}

After all these manipulations I have an approximate number of days till the user's deadline. That is Okay for me, because I do not need precision.

So, I passed this number of days to the other View and made a timer:

 let timer = Timer.publish(every: 86400, on: .main, in: .common).autoconnect()

 Text("\(dayString(time: Int(item.pickedValueDateN1))) days left")
                    .onReceive(timer){ _ in
                        if item.pickedValueDateN1 > 0 {
                           item.pickedValueDateN1 -= 1
                    }else{
                        self.timer.upstream.connect().cancel()
                    }    
                }

/////Function:
func dayString(time: Int) -> String {
    let days   = Int(time)
    return String(format:"%02i", days)
}

Moreover, I am calculating how many days has passed by subtracting item.pickedValueDateN1 out of item.pickedValueDateN (because one has all the deadline days and the other declining due to the timer).

So, to conclude and state my question:

  1. Everything works perfectly except the thing, that as soon as I leave the app - timer stops. I'd like the app to read how many days has passed since the time the goal was created and subtract that out of item.pickedValueDateN1. I read that I can do something using Date(). E.g. If user passes the app to the background, then returns and the app calculates how much has passed. it will be a nice one, however, I do not know how to implement it correctly.

  2. If You have any other suggestions, let me now please!

    var dateFromNow: Date = Calendar.current.date(byAdding: .second, value: pickedValueDateN, to: Date())!

    var numDaysWithToday: Int =
    Calendar.current.dateComponents([.second], from: Date(), to:
    dateFromNow).second!
    

Upvotes: 0

Views: 302

Answers (1)

lorem ipsum
lorem ipsum

Reputation: 29614

import SwiftUI

struct RelativeDateView: View {
    //Goal is set for 14 days from today
    @State var goalDate: Date = Date().addingTimeInterval(60*60*24*14-2)
    
    var body: some View {
        VStack{
            //The Text with style work even with widgets
            Text(goalDate, style: .relative)
            Text(goalDate, style: .offset)
            Text("days = \(goalDate.daysUntilToday)")
            Text("weeks = \(goalDate.weeksUntilToday)")
            Text("months = \(goalDate.monthsUntilToday)")
            Text("months = \(goalDate.daysMonthsUntilToday.months)  days = \(goalDate.daysMonthsUntilToday.days)")
        }
    }
}

struct RelativeDateView_Previews: PreviewProvider {
    static var previews: some View {
        RelativeDateView()
    }
}
extension Date{
    var daysUntilToday: Int{
        Calendar.current.dateComponents([.day], from: self, to: Date()).day ?? 0
    }
    var weeksUntilToday: Double{
        Double(self.daysUntilToday)/Double(7)
    }
    var monthsUntilToday: Int{
        Calendar.current.dateComponents([.month], from: self, to: Date()).month ?? 0
    }
    var daysMonthsUntilToday: (days: Int, months: Int){
        let components = Calendar.current.dateComponents([.day,.month], from: self, to: Date())
        return (components.day ?? 0, components.month ?? 0)
    }
}

Upvotes: 0

Related Questions