0

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!
    
  • Does this answer your question? [Calculating the number of days between two dates in swift](https://stackoverflow.com/questions/67854982/calculating-the-number-of-days-between-two-dates-in-swift) – lorem ipsum Jul 21 '21 at 23:15
  • @loremipsum That is not exactly what I am looking for. My question is how to subtract from `item.pickedValueDateN1` 1 day every 24 hours...even if the app is in the background. – Maksym Kucherenko Jul 22 '21 at 10:42
  • The timer thing won’t work. Your best bet is to use use a computed property by that uses your deadline and today and get the difference. You said you were open to another approach. Just a suggestion. – lorem ipsum Jul 22 '21 at 10:49
  • @loremipsum Okay, I got that. And how could I run it "in the background"?. I mean make some kind of checking how much time has passed since it has gone to the background and then subtract? – Maksym Kucherenko Jul 22 '21 at 10:57
  • A computed property will give you the right answer every time. No extra subtracting the display will always be right. What do you need it in the background for? – lorem ipsum Jul 22 '21 at 11:20
  • @loremipsum Oh, I finally got that. I thought that it requires some kind of checking when the app returns to the foreground. Thank you, man! – Maksym Kucherenko Jul 22 '21 at 11:26
  • @loremipsum , sorry for dumb question, but I have implemented the code you sent me. Now I would like to be sure that countdown works right. I changed everything to `.second` but the countdown hasn't started. What could be the problem? I added the code to my question – Maksym Kucherenko Jul 22 '21 at 15:59
  • The countdown hasn't started? What do you mean? The date should just display. Are you looking for a timer? – lorem ipsum Jul 22 '21 at 16:08
  • @loremipsum yep. I need to display a deadline and change it every 24 hours. Some kind of a timer. The problem with my previous timer was that it couldn't work when the app goes to background. E.g. you picked 7 days deadline and the next day it should be displayed as 6 days left and the day after 5 days and so on. – Maksym Kucherenko Jul 22 '21 at 16:11

1 Answers1

0
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)
    }
}
lorem ipsum
  • 21,175
  • 5
  • 24
  • 48