I\'m working on a prayers application that enables users to set an alarm(local notification) for prayer times, i.e. the user sets the application to notify him for Fajr pray
There are a few possible solutions for this. It might be safer to use an approach where a limited number of notifications are scheduled at a time, since iOS only keeps the 64 soonest notifications:
An app can have only a limited number of scheduled notifications; the system keeps the soonest-firing 64 notifications (with automatically rescheduled notifications counting as a single notification) and discards the rest.
Source: UILocalNotification class reference
It is also not a good idea to rely on using the UILocalNotification passed into application:didFinishLaunchingWithOptions:, since it is only passed when the user swipes the notification:
Look at the launch options dictionary to determine why your app was launched. The application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods provide a dictionary with keys indicating the reason that your app was launched.
The key value for launching in response to a local notification is:
UIApplicationLaunchOptionsLocalNotificationKey
Source: UIApplicationDelegate class reference
Option 1: schedule one day at a time (Code for this is provided below)
One way to handle the scheduling of notifications is to present a schedule to the user, where the day's notifications are scheduled at the time of the initial opening of the app.
Use a CustomNotificationManager class to handle notifications whose times are variable (code provided below). In your AppDelegate, you can delegate to this class the handling of the local notifications, which will either schedule the current day's notifications plus the following day's fixed-time notification, or respond to a prayer notification.
If the User opens the app in response to a prayer notification, the app can direct the user to an appropriate part of the app. If the user opens the app in response to the fixed-time notification, the app will schedule that day's local notifications according to the User's date and location.
Option 2 (Slightly slimmer approach, but which provides less to the User)
Another approach is to simply use a prayer notification's app launch to schedule the one that immediately follows. However, this is less reliable, and does not provide the ability to preview a schedule of notifications.
Notification Manager Header file
@interface CustomNotificationManager : NSObject
- (void) handleLocalNotification:(UILocalNotification *localNotification);
@end
Notification Manager Implementation file
#import "CustomNotificationManager.h"
#define CustomNotificationManager_FirstNotification @"firstNotification"
@implementation CustomNotificationManager
- (instancetype) init
{
self = [super init];
if (self) {
}
return self;
}
- (void) handleLocalNotification:(UILocalNotification *)localNotification
{
//Determine if this is the notification received at a fixed time,
// used to trigger the scheculing of today's notifications
NSDictionary *notificationDict = [localNotification userInfo];
if (notificationDict[CustomNotificationManager_FirstNotification]) {
//TODO: use custom algorithm to create notification times, using today's date and location
//Replace this line with use of algorithm
NSArray *notificationTimes = [NSArray new];
[self scheduleLocalNotifications:notificationTimes];
} else {
//Handle a prayer notification
}
}
/**
* Schedule local notifications for each time in the notificationTimes array.
*
* notificationTimes must be an array of NSTimeInterval values, set as intervalas
* since 1970.
*/
- (void) scheduleLocalNotifications:(NSArray *)notificationTimes
{
for (NSNumber *notificationTime in notificationTimes) {
//Optional: create the user info for this notification
NSDictionary *userInfo = @{};
//Create the local notification
UILocalNotification *localNotification = [self createLocalNotificationWithFireTimeInterval:notificationTime
alertAction:@"View"
alertBody:@"It is time for your next prayer."
userInfo:userInfo];
//Schedule the notification on the device
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
/* Schedule a notification for the following day, to come before all other notifications.
*
* This notification will trigger the app to schedule notifications, when
* the app is opened.
*/
//Set a flag in the user info, to set a flag to let the app know that it needs to schedule notifications
NSDictionary *userInfo = @{ CustomNotificationManager_FirstNotification : @1 };
NSNumber *firstNotificationTimeInterval = [self firstNotificationTimeInterval];
UILocalNotification *firstNotification = [self createLocalNotificationWithFireTimeInterval:firstNotificationTimeInterval
alertAction:@"View"
alertBody:@"View your prayer times for today."
userInfo:userInfo];
//Schedule the notification on the device
[[UIApplication sharedApplication] scheduleLocalNotification:firstNotification];
}
- (UILocalNotification *) createLocalNotificationWithFireTimeInterval:(NSNumber *)fireTimeInterval
alertAction:(NSString *)alertAction
alertBody:(NSString *)alertBody
userInfo:(NSDictionary *)userInfo
{
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
if (!localNotification) {
NSLog(@"Could not create a local notification.");
return nil;
}
//Set the delivery date and time of the notification
long long notificationTime = [fireTimeInterval longLongValue];
NSDate *notificationDate = [NSDate dateWithTimeIntervalSince1970:notificationTime];
localNotification.fireDate = notificationDate;
//Set the slider button text
localNotification.alertAction = alertAction;
//Set the alert body of the notification
localNotification.alertBody = alertBody;
//Set any userInfo, e.g. userID etc. (Useful for app with multi-user signin)
//The userInfo is read in the AppDelegate, via application:didReceiveLocalNotification:
localNotification.userInfo = userInfo;
//Set the timezone, to allow for adjustment for when the user is traveling
localNotification.timeZone = [NSTimeZone localTimeZone];
return localNotification;
}
/**
* Calculate and return a number with an NSTimeInterval for the fixed daily
* notification time.
*/
- (NSNumber *) firstNotificationTimeInterval
{
//Create a Gregorian calendar
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
//Date components for next day
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
dateComps.day = 1;
//Get a date for tomorrow, same time
NSDate *today = [NSDate date];
NSDate *tomorrow = [cal dateByAddingComponents:dateComps toDate:today options:0];
//Date components for the date elements to be preserved, when we change the hour
NSDateComponents *preservedComps = [cal components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:tomorrow];
preservedComps.hour = 5;
tomorrow = [cal dateFromComponents:preservedComps];
NSTimeInterval notificationTimeInterval = [tomorrow timeIntervalSince1970];
NSNumber *notificationTimeIntervalNum = [NSNumber numberWithLongLong:notificationTimeInterval];
return notificationTimeIntervalNum;
}
@end
AppDelegate didReceiveLocalNotification Implementation
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
CustomNotificationManager *notificationManager = [[CustomNotificationManager alloc] init];
[notificationManager handleLocalNotification:notification];
}
Suggestion for possible modification: If the CustomNotificationManager needs to maintain state, you could convert it to a Singleton.