SwiftUI find time elapsed between first time and now - date

I'm trying to do a timer in my app, but to have the ability to continue even if the user kill the app in the multitask I use #AppStorrage to store my initial time.
As it is not possible to save a DATE() in the #AppStorrage I save my initial hours:min into a String.
Then in my Timer function I'd like to compare the initial saved time with the current time using Calendar.current.dateComponents([.hour, .minute, .second], from: initialTime, to: Date())
I have this error on this line : Cannot assign value of type 'DateComponents' to type 'Double'
The code where I define the #AppStorage var :
#AppStorage("flightMode_DepTime") private var flightModeDepTime: String = ""
#ObservedObject var timerManager = TimerFunc()
var body: some View {
// ...
Button {
isStandbyMode.toggle()
if !isStandbyMode{
UserDefaults.standard.set(Date().formatted(.dateTime.hour().minute()), forKey: "flightMode_DepTime")
timerManager.start()
}else{
timerManager.pause()
}
} label: {
if isStandbyMode{
Image(systemName: "timer")
Text("STBY MODE ")
}else{
Image(systemName: "airplane")
Text("FLIGHT MODE ")
}
}
// ...
}
And here is my timer function :
#AppStorage("flightMode_DepTime") private var flightModeDepTime: String = ""
private var initialTime: Date = Date()
private var now: Date = Date()
func start() {
mode = .running
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) {timer in
let formatter = DateFormatter()
formatter.timeStyle = .medium
initialTime = formatter.date(from: self.flightModeDepTime) ?? Date()
self.secondsElapsed = Calendar.current.dateComponents([.hour, .minute, .second], from: initialTime, to: now)
}
}
Thanks for your help !

Related

HKStatistics Collection Query Only returning partial Data

Below is my query. When I print out the data in my ContentView section (below), accurate data from only the past week is shown and every other day is "nil" when I know for sure there is data for those days even when the predicate should go back 30 days.
func makeElevationQuery(completion: #escaping (HKStatisticsCollection?) -> ()) {
let elevationType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.flightsClimbed)!
let calendar = NSCalendar.current
let endDate = Date()
let startDate = calendar.date(byAdding: .day, value: -30, to: endDate)
let anchorDate = Date.mondayAt12AM()
let daily = DateComponents(day : 1)
let summariesWithinRange = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
elevationQuery = HKStatisticsCollectionQuery(quantityType: elevationType, quantitySamplePredicate: summariesWithinRange, options: .cumulativeSum, anchorDate: anchorDate, intervalComponents: daily)
elevationQuery!.initialResultsHandler = {elevationQuery, statisticsCollection, error in
completion(statisticsCollection)
}
healthstore.execute(elevationQuery!)
}
}
extension Date {
static func mondayAt12AM() -> Date {
return Calendar(identifier: .iso8601).date(from: Calendar(identifier: .iso8601).dateComponents([.yearForWeekOfYear, .weekOfYear], from: Date()))!
}
}
Content View Part:
struct ContentView: View {
#StateObject var fitness = main()
#State var final = HKActivitySummary()
#State var MonthElevation = HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier.flightsClimbed)
var body: some View {
Button("refresh"){
fitness.authorizeHealthkit()
fitness.makeQuery() { (summary) in
final = summary
fitness.makeElevationQuery { statisticsCollection in
if let statisticsCollection = statisticsCollection {
let startDate = Calendar.current.date(byAdding: .day, value: -30, to: Date())
let endDate = Date()
statisticsCollection.enumerateStatistics(from: startDate!, to: endDate) {
(statistics, stop) in
let count = statistics.sumQuantity()?.doubleValue(for: .count())
print(count) //prints out flights climbed on that day correctly for first week only but just "nil" another 23 times (because I had it go back a month)
}
}
}
}
Are you running this on watchOS or iOS? Sounds like you might be on watchOS since watch only has around a weeks worth of data. See earliestPermittedSampleDate.

How to make a timer that shows how much time has passed in SwiftUI?

I am trying to add a timer to the top of my view that shows how long that view has been open.
So far this is what I have:
#State var isTimerRunning = false
#State private var startTime = Date()
#State private var timerString = "0:0"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
However it shows milliseconds and seconds in the form "1.32434234234234" when I want it to display seconds and minutes in the form "12:43".
You can use an extension of TimeInterval for this. You can customize the string pretty well by changing the formatter.unitsStyle (.positional will show 00:00:00, while .abbreviated will show 0h 0m 0s) and the formatter.zeroFormattingBehavior variables.
Credits for the extension found here.
extension TimeInterval {
func format(using units: NSCalendar.Unit) -> String {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = units
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
return formatter.string(from: self) ?? ""
}
}
struct TimerPlayground: View {
#State var isTimerRunning = false
#State private var startTime = Date()
#State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(interval.format(using: [.hour, .minute, .second]))
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
struct TimerPlayground_Previews: PreviewProvider {
static var previews: some View {
TimerPlayground()
}
}
Inline with #Duncan C's comment, here's an updated version which creates the formatter only once (locally) for better performance.
struct TimerPlayground: View {
#State var isTimerRunning = false
#State private var startTime = Date()
#State var interval = TimeInterval()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#State var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .pad
return formatter
}()
var body: some View {
Text(formatter.string(from: interval) ?? "")
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
interval = Date().timeIntervalSince(startTime)
}
}
.onAppear() {
if !isTimerRunning {
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
Use dateComponents to get the difference between start time and the current time.
extension Date {
func passedTime(from date: Date) -> String {
let difference = Calendar.current.dateComponents([.minute, .second], from: date, to: self)
let strMin = String(format: "%02d", difference.minute ?? 00)
let strSec = String(format: "%02d", difference.second ?? 00)
return "\(strMin):\(strSec)"
}
}
And in view
struct ContentView: View {
#State private var isTimerRunning = false
#State private var startTime = Date()
#State private var timerString = "00:00"
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = Date().passedTime(from: startTime)
}
}
.onAppear() {
if !isTimerRunning {
timerString = "0:0"
startTime = Date()
}
isTimerRunning.toggle()
}
}
}

reset date to next hour after top of hour is reached

I Want My swift code to count down to the nearest top of the hour. So if the time is 146 the user code should count down 14 minutes. Right now My code below counts down to a spefic day and time. I just want it to count down to the nearest hour when the app is running.
import UIKit
class ViewController: UIViewController {
#IBOutlet var timerLabel: UILabel!
var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
}
#objc func UpdateTime() {
let userCalendar = Calendar.current
// Set Current Date
let date = Date()
let components = userCalendar.dateComponents([.hour, .minute, .month, .year, .day], from: date)
let currentDate = userCalendar.date(from: components)!
// Set Event Date
var eventDateComponents = DateComponents()
eventDateComponents.year = 2021
eventDateComponents.month = 01
eventDateComponents.day = 01
eventDateComponents.hour = 01
eventDateComponents.minute = 00
eventDateComponents.timeZone = TimeZone(abbreviation: "GMT")
let eventDate = userCalendar.date(from: eventDateComponents)!
let timeLeft = userCalendar.dateComponents([.day, .hour, .minute, ], from: currentDate, to: eventDate)
timerLabel.text = "\(timeLeft.day!)d \(timeLeft.hour!)h \(timeLeft.minute!)m "
endEvent(currentdate: currentDate, eventdate: eventDate)
}
func endEvent(currentdate: Date, eventdate: Date) {
if currentdate >= eventdate {
timerLabel.text = "Happy New Year!"
// Stop Timer
timer.invalidate()
}
}
}
edit/update:
My goal in my swift code is when the top of the hour is reached. After trying to implement #Leo's answer it prints "Top of Hour" and it does the problem is that It only does it one time. As long as the app is open I want it to print "Top of Hour" at every hour. So I need to reset the end date which is what I tried to do at
let date = Date()
end = date.nextHour
That does not let the code compile. So I have to reset the end var to the next hour.
No need to update the user interface 10 times per second. As it is it will drain the device's battery much faster than needed while it should only run once a minute. You can change your timer timeInterval to 1 second and schedule it to fire at the next even second. To get the next even hour and the next even minute you can use Calendar method
func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?
Just create two computed properties extending Date and pass zero for minute or nanosecond components:
extension Date {
var nextHour: Date {
Calendar.current.nextDate(after: self, matching: DateComponents(minute: 0), matchingPolicy: .strict)!
}
var nextSecond: Date {
Calendar.current.nextDate(after: self, matching: DateComponents(nanosecond: 0), matchingPolicy: .strict)!
}
var minute: Int {
Calendar.current.component(.minute, from: self)
}
}
Now add a property to your view controller to keep a reference of the end date. Note that there is no need to declare your timer as optional:
var end: Date?
var timer = Timer()
And create a DateComponentsFormatter to create a localized description of the remaining time:
extension Formatter {
static let minutesRemaining: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.formattingContext = .standalone
formatter.unitsStyle = .short
formatter.allowedUnits = [.minute, .second]
formatter.includesTimeRemainingPhrase = true
return formatter
}()
}
Now you just setup the end date and to schedule your timer to fire at the next even minute:
override func viewDidLoad() {
super.viewDidLoad()
// get the current date
let date = Date()
// set the end date
end = date.nextHour
// schedule the timer to fire at the next even second and set its interval to 1 second
timer = .init(fireAt: date.nextSecond, interval: 1, target: self, selector: #selector(updateUI), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .common)
updateUI()
}
#objc func updateUI() {
if Date().minute == 0 || Date() > end {
end = Date().nextHour
timerLabel.text = "beginning of hour"
print("beginning of hour")
} else {
// update the remaining time (for a perfect sync we need to subtract a second from the current time)
let text = Formatter.minutesRemaining.string(from: Date().addingTimeInterval(-1), to: end) ?? ""
timerLabel.text = text
print(text)
}
}

Check if today is a new day SwiftUI

How can I check if today is a new day?
How can I say when the app lunches or in background, whichever, if it's 8am of a new day then do some action...
Date() would only give me the current date.
I need this to do some resetting and possibly to send notification at a specific time
you could try something like this:
import SwiftUI
struct ContentView: View {
#State var lastDay: Date = Date()
#State var isToday = false
#State var selectedTime = 8 // 24 hour clock
var body: some View {
Text("your main view")
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
// store the date whenever you go into background
print("---> storing: \(Date())")
UserDefaults.standard.set(Date(), forKey: "lastDay")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
// try to retrieve the date when you come back from background
print("\n----> try retrieve lastDay")
if let temDate = UserDefaults.standard.object(forKey: "lastDay") {
self.lastDay = temDate as! Date
print("----> retrieved lastDay: \(self.lastDay)")
if Calendar.current.isDate(Date(), inSameDayAs: self.lastDay) {
self.isToday = true
print("----> isToday: \(self.isToday)\n")
// if it is 8 am or later do something
if let thisHour = Calendar.current.dateComponents([.hour], from: Date()).hour {
if (thisHour >= self.selectedTime) {
print("----> it is 8am or later --> do something")
// self.doSomething()
}
}
}
}
}
}
}
NotificationCenter is Apple internal message system. SwiftUI can listen for specific events, like when the App goes into the background. This is what this line does:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification))
Now, this line:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification))
listens for when you come back from the background, as mentioned in the comments.
There is no slowing down because of these onReceives.
What I have shown is an approach, you can add other .onReceive, such as:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)) { _ in
These can be used for when you start the App, and when you quit the App.
So this all should work to be able to tell if the date has changed.
struct ContentView: View {
#State private var lastDateString = UserDefaults.standard.string(forKey: "lastDateString") ?? String()
//stores lastDateString in memory for if the app closes
#State var lastDate = Date()
#State var currentDate = Date()
#State var currentDateString = String()
#State var differentDate = false;
let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
//Creates a timer that goes every 1 second
func newDay() {
let formatter = DateFormatter()
formatter.dateFormat = "d MM y"
//sets the format so it will be day month year
if lastDateString == String() {
lastDateString = formatter.string(from: lastDate)
UserDefaults.standard.set(self.lastDateString, forKey: "lastDateString")
} //sets initial value for lastDateString for first time app ever launches
self.currentDate = Date()
currentDateString = formatter.string(from: currentDate)
//sets currentDateString for every time app launches
if lastDateString != currentDateString {
self.differentDate = true
self.lastDate = Date()
self.currentDate = Date()
lastDateString = formatter.string(from: lastDate)
UserDefaults.standard.set(self.lastDateString, forKey: "lastDateString")
currentDateString = formatter.string(from: currentDate)
}
//checks if the date has changed and sets differentDate to true if it has
}
var body: some View {
VStack{
Text("Hello, World!")
.onAppear(){ //used to run function when app launches
self.newDay()
}
}
.onReceive(self.timer) { _ in
self.newDay()
} //calls function newDay() every second from timer
}
}
Hope this helps and sorry that this may not be the most concise, but hopefully it helps.
if Calendar.current.isDate(lastVisit as! Date, inSameDayAs: Date()) {
print("Same day")
} else {
print("New day")
}

Local Notification every X day - Swift

Before I begin please don't burn me as I know this has been asked hundreds of times on here with no reliable answer but I believe there's a solution using background refresh. https://medisafe.com/ app seems to have solved it!
The goal :
To trigger a local notification at a specified time every x days
My solution
step 1: get timer interval from start date and odd occurrence (this case 2) days from (edited)
step 2: set interval timer on this difference with a repeat
step 3: activate background refresh ( if the app is even terminated it will load the app in the background and give me a small window to perform some tasks)
step 4. set background refresh to trigger once a day
step 5: perform get items api which will refresh all timers and notifications
step 6 sit back and smile with amazement at my solution
but this fails.
so a timer interval
let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,repeats: true)
but this will just reset the timer every day when the background fetch is performed and it will trigger 2 days from NOW and not from the start date.
So there must be a way of comparing dates hours and minutes (start date, x date, and current date to work out the timer interval value.
currently im using calendar components. to trigger everyday im doing the following
var triggerType : DateComponents? {
var triggerT : DateComponents?
var cal = Calendar(identifier: .gregorian)
cal.firstWeekday = 2
if let notificationModel = self.notificationModel {
switch notificationModel.reminderType {
case .daily?, .weekly?:
if let date = notificationModel.date {
triggerT = cal.dateComponents([.weekday, .hour, .minute], from:date)
if let weekday = notificationModel.weekday {
triggerT?.weekday = weekday
}
}
case .alternateDays?:
if let date = notificationModel.date {
triggerT = cal.dateComponents([ .hour, .minute], from:date)
// THIS IS WHERE I NEED HELP
}
case .monthly?:
if let date = notificationModel.date {
triggerT = cal.dateComponents([.day,.hour,.minute], from: date)
}
case .yearly?:
triggerT = Calendar.current.dateComponents([.month,.day,.hour,.minute], from: (notificationModel.date)!)
case .oneOff?:
triggerT = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute], from: (notificationModel.date)!)
case .none:
DispatchQueue.main.async {
if let category = self.notificationModel?.category, let title = self.notificationModel?.title {
Toast.down("An error was discovered in \(category). Please change the occurance value for the following \(title)")
}
}
}
} else {
print("NOTIFICATION MODEL IS CORRUPT")
}
return triggerT
}
func add(notification: NotificationModel){
let content = UNMutableNotificationContent()
if let title = notification.title,
let body = notification.body,
let identifier = notification.identifier {
content.title = title
content.body = body
content.sound = UNNotificationSound.default()
content.categoryIdentifier = (notification.category?.rawValue)!
content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")
var trigger : UNCalendarNotificationTrigger?
if let triggerType = self.triggerType {
if let occurance = notification.occurance {
if occurance > 0 {
}
}
trigger = UNCalendarNotificationTrigger(dateMatching: triggerType, repeats: true)
} else {
return
}
let interval = Date().timeIntervalSince1970
let identifierString = "2\(interval)"
var request : UNNotificationRequest!
if notification.reminderType == .alternateDays {
print("ADDING TIMER NOTIFICATION")
print("REMINDER TIME = \(notification.date)")
// 172800 = two days
let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,
repeats: true)
request = UNNotificationRequest(identifier: identifierString,
content: content, trigger: newTrigger)
} else {
request = UNNotificationRequest(identifier: identifierString,
content: content, trigger: trigger)
}
center.add(request, withCompletionHandler: { (error) in
if let error = error {
// Something went wrong
print(error.localizedDescription)
} else
{
print("ADDING NOTIDCIATION \(content.title)")
}
})
//SNOOZE OR DELETE NOTIFICATIONS
let snoozeAction = UNNotificationAction(identifier: "Snooze", title: "Snooze", options: [])
let deleteAction = UNNotificationAction(identifier: "UYLDeleteAction",title: "Delete", options: [.destructive])
//Create a category with the actions: This requires another unique identifier (you probably want to define these magic strings in an enum):
let category = UNNotificationCategory(identifier: notification.category!.rawValue,
actions: [snoozeAction,deleteAction],
intentIdentifiers: [], options: [])
//Register the category with the notification center. It is recommended to do this early in the app lifecycle.
center.setNotificationCategories([category])
//To include this action in our notifications we need to set the category in the notification content:
} else {
print("Failed to add notification")
}
}
however, I want every other day and dont want to use the 64 notification limit.
thanks for your time
Thomas
Lets say you want to trigger notification 2, 4 and 6 days from now, here is how you can do it:
For my example I added extension to Date
extension Date {
func adding(days: Int) -> Date? {
var dateComponents = DateComponents()
dateComponents.day = days
return NSCalendar.current.date(byAdding: dateComponents, to: self)
}
}
Then you could just create new notifications for dates specified, in this example 2, 4, 6 days from now
let date = Date()
for i in [2, 4, 6] {
if let date = date.adding(days: i) {
scheduleNotification(withDate: date)
}
}
func scheduleNotification(withDate date: Date) {
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "Title"
notificationContent.subtitle = "Subtitle"
notificationContent.body = "Body"
let identifier = "Make up identifiers here"
let dateComponents = Calendar.autoupdatingCurrent.dateComponents([.day, .month, .year, .hour, .minute, .second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let notificationReques = UNNotificationRequest(identifier: identifier, content: notificationContent, trigger: trigger)
UNUserNotificationCenter.current().add(notificationReques) { error in
if let e = error {
print("Error \(e.localizedDescription)")
}
}
}
This should schedule 3 notifications - 2, 4, 6 days from now...
So thanks for the directions on here this is the final solution i came up with. Ensure you turn on background modes in app capabilities so the current week is updated. i did mine to every day.
Then the code with comments.
//: Playground - noun: a place where people can play
import UIKit
import UserNotifications
Lets create some helper clases to make it easier to work with dates
// HELPERS
extension Date {
public var weekday: Int {
return Calendar.current.component(.weekday, from: self)
}
public var hour: Int {
get {
return Calendar.current.component(.hour, from: self)
}
set {
let allowedRange = Calendar.current.range(of: .hour, in: .day, for: self)!
guard allowedRange.contains(newValue) else { return }
let currentHour = Calendar.current.component(.hour, from: self)
let hoursToAdd = newValue - currentHour
if let date = Calendar.current.date(byAdding: .hour, value: hoursToAdd, to: self) {
self = date
}
}
}
public var minute: Int {
get {
return Calendar.current.component(.minute, from: self)
}
set {
let allowedRange = Calendar.current.range(of: .minute, in: .hour, for: self)!
guard allowedRange.contains(newValue) else { return }
let currentMinutes = Calendar.current.component(.minute, from: self)
let minutesToAdd = newValue - currentMinutes
if let date = Calendar.current.date(byAdding: .minute, value: minutesToAdd, to: self) {
self = date
}
}
}
}
Then we create our custom notification struct
struct CustomNotification {
static func everyOtherDay(wtihStartDate startDate: Date) -> [Int]? {
//
let currentDate = Date()
// get initial week day from start date to compare dates
let weekDay = startDate.weekday
// Then we need to get week of years for both dates
let cal = Calendar.current
guard let weekA = cal.dateComponents([.weekOfYear], from: startDate).weekOfYear else { return nil}
guard let weekB = cal.dateComponents([.weekOfYear], from: currentDate).weekOfYear else {return nil}
// create two arrays for week days
let weekOne = [1,3,5,7]
let weekTwo = [2,4,6]
// then we create a module to check if we are in week one or week two
let currentWeek = (weekA - weekB) % 2
if currentWeek == 0 {
//week 1
return weekOne.contains(weekDay) ? weekOne : weekTwo
} else {
// week 2
return weekOne.contains(weekDay) ? weekTwo : weekOne
}
}
}
finally in our class where we create the notification. I personally use a notification manager. but to shwo you quickly
class AClass : NSObject {
func setupNotifications() {
let startDate = Date()
let weekDays = CustomNotification.everyOtherDay(wtihStartDate: startDate)
let cal = Calendar.current
let center = UNUserNotificationCenter.current()
if let weekDays = weekDays {
for day in weekDays {
let identifier = "Some Random ID"
let content = UNMutableNotificationContent()
content.title = "title"
content.body = "body"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "SOME CATEGORY"
content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")
var components = cal.dateComponents([.hour, .minute], from:startDate)
components.weekday = day
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if let error = error {
// Something went wrong
print("ERROR ADDING NOTIFICATION TO CENTER \(error.localizedDescription)")
} else
{
print("ADDING NOTIFCIATION \(content.categoryIdentifier)")
}
})
}
}
}
}
Then we need to setup background fetch in our app and app delegate
// OVER IN APP DELEGATE
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// setup background refresh ensuring you turn it on in app capabilities
// trigger back ground refrsh once a day
UIApplication.shared.setMinimumBackgroundFetchInterval(86400)
return true
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// FETCH DATA and REFRESH NOTIFICATIONS
// We need to do this to ensure the current week value is updated to either 1 or 0
// You will need to delete all notifications with same same category first else your going to be getting both weeks notifications
let aClass = AClass()
aClass.setupNotifications()
}
Hope this helps somebody :D Thomas