Beginner trying to create an app that gives the user a random "word of the day".
Through testing I realised that the app has to be opened every day by the user in order to manually trigger the function isRefreshRequired()
(which retrieves the new word and updates user interface and notification content). But I need this to be done automatically, regardless of whether the user opens the app or not.
Is this even possible? I am stuck between which methods to use:
NSCalendarDayChanged. I found this useful answer but I am not sure where it would be implemented? If I use it applicationDidFinishLaunching, where would the function calendarDayDidChange() be called? In viewDidLoad()?
significantTimeChangeNotification() - Possibly a better method? Again, not sure how to actually implement this and where. The document also says:
If the the device is asleep when the day changes, this notification will be posted on wakeup.
Does wakeup mean when the app is reopened? Because I want the isRefreshRequired() function to execute even if the user hasn't touched the app.
The code in question:
override func viewDidLoad() {
super.viewDidLoad()
isRefreshRequired()
}
func isRefreshRequired() {
// if it is first time opening app
if userDefaults.bool(forKey: "First Launch") == false {
//run code during first launch
_ = updateWordOfTheDay()
NotificationManager.askNotificationPermission()
print("First time opening app")
userDefaults.set(true, forKey: "First Launch")
}
else {
//run code after first launch
print("Not first time opening app")
userDefaults.set(true, forKey: "First Launch")
let lastAccessDate = userDefaults.object(forKey: "lastAccessDate") as? Date ?? Date()
userDefaults.set(Date(), forKey: "lastAccessDate")
// if it is the same day give the vocab picked for that day
if calendar.isDateInToday(lastAccessDate) {
readFromUserDefaults()
print("No refresh required as it is the same day as lastAccessDate which was \(lastAccessDate)")
}
else {
print("new day since lastAccessDate")
_ = updateWordOfTheDay()
}
}
}
func sendNotification(using: ChineseVocab) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications()
center.removeAllPendingNotificationRequests()
let content = UNMutableNotificationContent()
content.title = using.Chinese + " " + using.Pinyin
content.body = using.Definition
}
func saveToUserDefaults(with data: ChineseVocab) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(data) {
userDefaults.set(encoded, forKey: "selectedVocab")
}
}
func readFromUserDefaults() {
if let savedVocab = userDefaults.object(forKey: "selectedVocab") as? Data {
let decoder = JSONDecoder()
if let wordOfTheDay = try? decoder.decode(ChineseVocab.self, from: savedVocab) {
updateLabels(using: wordOfTheDay)
NotificationManager.sendNotification(using: wordOfTheDay)
print("readFromUserDefaults() is working: \(wordOfTheDay)")
} else {
print("unable to load readFromUserDefaults()")
}
}
}
What you are trying to do is call a function when the app is closed or in the background. Taking a step back, the user isn't actually using your app at this time. So because of that, your app shouldn't really be running functions either. I believe this is done on purpose so that the phone can allocate resources to apps/functions that the user is actively using.
So there is no way to just call a function when the app is closed. But going off your points above:
I think using applicationDidBecomeActive or applicationDidFinishLaunching is probably your best bet. These are functions within the AppDelegate and you can call the functions within the methods directly. The applicationDidBecomeActive will execute every time the app appears on the screen. If you're only need it on one screen, you may be able to just call in the viewDidAppear / viewWillAppear of the ViewController instead.
Idk anything about significantTimeChangeNotification so I don't want to tell you it won't work, but I would guess the notification would not execute if the app is closed.
Background App Refresh is basically what you are trying to do, however, even if you implement it.. it will only execute when the app is the background. If the app is completely closed, the background fetch won't be called. So I would assume that most of the time it wouldn't be called anyway.