Search code examples
iosswiftswiftuiapple-push-notificationsuilocalnotification

Local notification not firing when multiple days set


I'm trying to create a repeating local notification for specific days of the week, but at the same time on those days.

The app will allow the user to set multiple local notifications for different list items; for example:

Item 1: Sunday, Monday, Tuesday - 9:00am
Item 2: Monday, Wednesday - 6:00pm
Item 3: Monday, Tuesday - 9:00am
Item 4: Friday - 12:00pm

So there is the ability to have multiple notifications set for the same day or same time.

At the moment, when I set the schedule for single days (example Item #4) it works correctly. The notification pops up.

When I schedule more than one day, it seems the notification doesn't register, or doesn't come through.

I've been trawling though the code to see if I can see where but for the life of me cannot see where it won't be set.

This is a rudimentary code, but it does demonstrate how it is set up, and how it is not working:

import SwiftUI
import UserNotifications

struct AlarmModel {
 let id = UUID().uuidString
 let title: String
 let subtitle: String
 let body: String
 let days: [Int]
 let hour: Int
 let minute: Int
}

struct ContentView: View {
 @State var array = [1, 2, 3, 4, 5, 6, 7]
 @State private var selectedDays: [Int] = []
 @State private var currentTime: Date = Date()
 let nc = NotificationManager()

 var body: some View {
  Form {
   Section {
    HStack(spacing: 10) {
     ForEach(array, id: \.self) { day in
      Text("\(day)")
       .bold()
       .frame(maxWidth: .infinity, maxHeight: .infinity)
       .foregroundColor(.white)
       .background(
        Circle()
         .foregroundColor(
          selectedDays.contains(day) ?
           .accentColor : .gray
         )
       )
       .onTapGesture { checkActiveDays(day) }
     }
    }
    .frame(maxWidth: .infinity, minHeight: 50)
   } header: {
    Text("Days of the week - 1=Sunday, 7=Saturday")
   } footer: {
    Text("1 = SUN / 2 = MON / 3 = TUES / 4 = WED / 5 = THURS / 6 = FRI / 7 = SAT")
   }

   Section {
    DatePicker("Alert time", selection: $currentTime, displayedComponents: .hourAndMinute)
   } header: {
    Text("Select alert time")
   }

   Section {
    Button {
     saveNotification()
    } label: {
     Text("Set alert")
    }

   }
  }
 }
 private func checkActiveDays(_ index: Int) {
  if( selectedDays.contains(index) ) {
   selectedDays.remove(at: selectedDays.firstIndex(of: index)!)
  } else {
   selectedDays.append(index)
  }
 }

 private func saveNotification() {
  let alarm = AlarmModel(
   title: "Test alert title",
   subtitle: "Test subtitle",
   body: "Test body text",
   days: selectedDays,
   hour: Calendar.current.component(.hour, from: currentTime),
   minute:  Calendar.current.component(.minute, from: currentTime)
  )

  // -- add it
  nc.addNotification(alarm: alarm)
 }
}


final class NotificationManager {
 var notifications: [Notification] = []
 let nc = UNUserNotificationCenter.current()

 init() {
  nc.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
   if granted == true && error == nil {
    print("Notifications permitted")
   } else {
    print("Notifications not permitted")
   }
  }
 }

 private func createNotification(id: String, weekDay: Int, hour: Int, minute: Int, content: UNNotificationContent) {

  var dateComponents = DateComponents()
  dateComponents.weekday = weekDay
  dateComponents.hour = hour
  dateComponents.minute = minute

  let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
  let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)

  nc.add(request) { error in
   if let error = error { print("Error \(error.localizedDescription)") }
  }
 }

 func addNotification(alarm: AlarmModel) {
  nc.removePendingNotificationRequests(withIdentifiers: [alarm.id])

  let content = UNMutableNotificationContent()
  content.title = alarm.title
  content.subtitle = alarm.subtitle
  content.body = alarm.body
  content.sound = .default

  if(alarm.days.count > 0) {
   for day in alarm.days {
    createNotification(
     id: alarm.id,
     weekDay: day,
     hour: alarm.hour,
     minute: alarm.minute,
     content: content
    )
   }
  }
 }
}

Solution

  • The id for each notification in the loop has to be unique.

    You are using the same id for all the notifications. The newest overrides the previous.

    Add something unique to the id String at the end, like the index or another UUID or the day description.