I've previously tried many times to figure out a way to send out local push notifications of quotes from my own API (connected to my own database)—might be useful info in case there's some change I could make to the API.
My goal is to send out a daily quote at the user's specified time during the day, with different quote info, fetched by the getRandomQuoteByClassification()
call. This function gets a quote based on the user's selected quote category, which could change at any time. As such, I'd want to only schedule/send one notification at a time with the specified quote category—unlike other apps with a constant notification to send out at a specified time, I want this notification to be dynamic.
Currently, the quote sends out fine the first time, but simply sends out the exact same notification at the same time the next day.
I've read up on answers such as this one, which mention how I would need to send out a remote notification call, as opposed to a local one. In practice, though, I could really use some guidance on how to incorporate this into my current implementation below:
private func scheduleNotifications() {
// Cancel existing notifications to reschedule them with the new time
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
// Get the selected time from notificationTime
let selectedTime = Calendar.current.dateComponents([.hour, .minute], from: notificationTime)
// Create a trigger date for the selected time
guard let triggerDate = Calendar.current.date(from: selectedTime) else {
print("Error: Couldn't create trigger date.")
return
}
// Create a date components for the trigger time
let triggerComponents = Calendar.current.dateComponents([.hour, .minute], from: triggerDate)
// Create a trigger for the notification to repeat daily at the selected time
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerComponents, repeats: true)
// Retrieve a new quote
getRandomQuoteByClassification(classification: getSelectedQuoteCategory().lowercased()) { quote, error in
if let quote = quote {
// Create notification content
let content = UNMutableNotificationContent()
if getSelectedQuoteCategory() == QuoteCategory.all.rawValue {
content.title = "Quote Droplet"
} else {
content.title = "Quote Droplet - \(getSelectedQuoteCategory())"
}
if let author = quote.author, !author.isEmpty {
if author == "Unknown Author" {
content.body = quote.text
} else {
content.body = "\(quote.text)\n- \(author)"
}
} else {
content.body = quote.text
}
content.sound = UNNotificationSound.default
// Generate a unique identifier for this notification
let notificationID = UUID().uuidString
// Create notification request
let request = UNNotificationRequest(identifier: notificationID, content: content, trigger: trigger)
// Schedule the notification
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
} else {
print("Notification scheduled successfully.")
print("Body of notification scheduled: \(content.body)")
print("Scheduled for this time: \(selectedTime)")
}
}
} else if let error = error {
print("Error retrieving quote: \(error.localizedDescription)")
} else {
print("Unknown error retrieving quote.")
}
}
}
I'd greatly appreciate any code suggestions or feedback on my thinking through this problem. Thanks.
I simply ended up scheduling local notifications, using local data (a JSON file I have within the project). Note that you can only schedule 64 local notifications (see this Apple article for more info). Then, I believe you'll need the user to have re-launched the app for more to be scheduled.
Here's the code if it's useful to anyone:
func scheduleNotifications(notificationTime: Date, quoteCategory: QuoteCategory) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
let classification = quoteCategory.displayName
let calendar = Calendar.current
let currentDate = calendar.startOfDay(for: Date())
// 60, meaning 60 days
for i in 0..<60 {
var triggerDate = calendar.dateComponents([.hour, .minute], from: notificationTime)
triggerDate.day = calendar.component(.day, from: currentDate) + i
let content = UNMutableNotificationContent()
let shortQuotes = quotes.filter{ $0.text.count <= 100 }
var randomQuote: QuoteJSON
if classification.lowercased() == "all" {
guard let randomElement = shortQuotes.randomElement() else {
print("Error: Unable to retrieve a random quote.")
continue
}
randomQuote = randomElement
content.title = "Quote Droplet"
} else if classification.lowercased() == "favorites" {
let bookmarkedQuotes = getBookmarkedQuotes().map { $0.toQuoteJSON() }
if !bookmarkedQuotes.isEmpty {
let randomIndex = Int.random(in: 0..<bookmarkedQuotes.count)
let randomElement = bookmarkedQuotes[randomIndex]
randomQuote = randomElement
content.title = "Quote Droplet: Favorites"
} else {
randomQuote = QuoteJSON(id: 9999999, text: "Please add a quote to favorites by clicking the favorites button under a quote in the app's \"Droplets\" tab", author: "", classification: "Favorites")
content.title = "Quote Droplet: No Favorites Added"
}
} else {
let filteredQuotes = shortQuotes.filter { $0.classification.lowercased() == classification.lowercased() }
guard let randomElement = filteredQuotes.randomElement() else {
print("Error: Unable to retrieve a random quote.")
continue
}
randomQuote = randomElement
content.title = "Quote Droplet: \(classification)"
}
if (randomQuote.author != "Unknown Author" && randomQuote.author != "" && randomQuote.author != "NULL" && ((randomQuote.author.isEmpty))) {
content.body = "\"\(randomQuote.text)\"\n— \(randomQuote.author)"
} else {
content.body = "\"\(randomQuote.text)\""
}
content.sound = UNNotificationSound.default
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let notificationID = UUID().uuidString
let request = UNNotificationRequest(identifier: notificationID, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
} else {
print("Notification scheduled successfully.")
print("Body of notification scheduled: \(content.body)")
print("Scheduled for this time: \(triggerDate)")
}
}
}
}