我正在做一个祷告应用程序,使用户能够为祷告时间设置警报(本地通知),即用户设置应用程序每天通知他Fajr祷告,问题是时间每个祷告每天都在变化,所以应用程序将在周四通知用户公平的时间与星期五的时间不同,我需要每天重复本地通知,但是根据每日祷告时间,请有人能给我一个想法?
答案 0 :(得分:6)
有一些可能的解决方案。使用一次安排有限数量的通知的方法可能更安全,因为iOS仅保留64个最快的通知:
应用只能有有限数量的预定通知;系统保持最快的64个通知(自动重新安排的通知计为单个通知)并丢弃其余通知。
来源:UILocalNotification类引用
依靠使用传递给UILocalNotification
的{{1}}也不是一个好主意,因为它只在用户滑动通知时传递:
查看启动选项字典,以确定启动应用的原因。应用程序:willFinishLaunchingWithOptions:和application:didFinishLaunchingWithOptions:方法提供一个字典,其中包含指示应用程序启动原因的键。
响应本地通知而启动的关键值是:
application:didFinishLaunchingWithOptions:
来源:UIApplicationDelegate类引用
选项1:一次安排一天(以下提供此代码)
处理通知计划的一种方法是向用户提供计划,其中在应用程序首次打开时安排当天的通知。
使用UIApplicationLaunchOptionsLocalNotificationKey
类来处理时间可变的通知(下面提供的代码)。在您的AppDelegate中,您可以委派此课程处理本地通知,该通知将安排当天的通知加上第二天的固定时间通知,或者回复祷告通知。
如果用户打开应用以响应祷告通知,则应用可以将用户引导至应用的相应部分。如果用户打开应用程序以响应固定时间通知,则应用程序将根据用户的日期和位置安排当天的本地通知。
选项2(略微更轻薄的方法,但对用户提供的更少)
另一种方法是简单地使用祷告通知的应用程序启动来安排紧随其后的那个。但是,这不太可靠,并且无法预览通知计划。
通知管理器标头文件
CustomNotificationManager
通知管理器实施文件
@interface CustomNotificationManager : NSObject
- (void) handleLocalNotification:(UILocalNotification *localNotification);
@end
AppDelegate didReceiveLocalNotification Implementation
#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
建议可能的修改:如果CustomNotificationManager需要维护状态,您可以将其转换为Singleton。
答案 1 :(得分:2)
所以问题出现了,您需要不时地设置此本地通知,但不能是可重复的通知。我假设用户设定了祈祷时间,并希望得到通知。我建议你设置一些,因为你从列表中知道。然后设置后台提取,让我们说每5个小时,并在应用程序后台启动时,只检查仍然设置的本地通知,并根据当前日期相应地更新列表。在这种情况下,后台提取不会每5小时精确唤醒您的应用程序,但会尽力而为。我相信你的应用程序每天至少会醒来两次。您可以根据自己的需要调整时间。
有机会获取少量内容 需要定期检查新内容的应用程序可以要求系统将其唤醒,以便他们可以启动对该内容的获取操作。要支持此模式,请从Xcode项目的“功能”选项卡的“后台模式”部分启用“后台获取”选项。 (您还可以通过在应用程序的Info.plist文件中包含带有提取值的UIBackgroundModes键来启用此支持。)启用此模式并不能保证系统会随时为您的应用程序执行后台提取。系统必须平衡您的应用程序根据其他应用程序和系统本身的需求获取内容的需求。在评估该信息后,系统会在有良好机会的情况下为应用程序提供时间。当出现好机会时,系统会将应用程序唤醒或启动到后台并调用应用程序委托的应用程序:performFetchWithCompletionHandler:method。如果内容可用,请使用该方法检查新内容并启动下载操作。完成下载新内容后,必须执行提供的完成处理程序块,并传递指示内容是否可用的结果。执行此块会告诉系统它可以将您的应用程序移回暂停状态并评估其耗电量。快速下载少量内容并准确反映其内容可供下载的应用程序,比将需要很长时间下载内容或声明内容可用的应用程序更有可能在未来获得执行时间不下载任何东西。
有关更多信息,请参阅Apple关于后台执行的文档:
答案 2 :(得分:1)
有三种方法可以做到这一点:
使用推送通知而不是本地通知,并将逻辑移至服务器。问题 - 离线时用户不会收到通知。
继续使用本地通知。您必须为每个祷告时间计划一个新通知。当然,本地通知的数量是有限的(最多64
预定通知),但对于一周的通知应该足够了。通知不是警报,用户应该响应于接收通知而打开应用程序。这样,您可以在重新打开应用程序时重新安排所有通知。此外,最后一个通知可能类似于“您暂时没有打开应用程序,您将不会收到更多通知”。
不是创建本地通知,而是在设备日历中创建闹钟/提醒(Event Kit)
答案 3 :(得分:-1)
到目前为止,我发现最好的方法是安排接下来的12天(12天* 5个通知= 60个通知)的祈祷时间。
请注意,iOS不允许每个通知安排超过64条通知 应用程序。
用户打开应用后,我会删除所有剩余的通知,并在接下来的12天中重新安排新的通知。
重要的是,将Background Fetch(工作)添加到您的应用程序中。在 AppDelegate 类中,添加以下代码:
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Should schedule new notifications from background
PrayerTimeHelper().scheduleNotifications()
completionHandler(.newData)
}
像这样修改 didFinishLaunchingWithOptions 方法:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Setup Fetch Interval
//UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
UIApplication.shared.setMinimumBackgroundFetchInterval(12 * 3600) // launch each 12 hours
}
以下是安排12天通知的方法:
/// Schedule notifications for the next coming 12 days.
/// This method is also called by Background Fetch Job
func scheduleNotifications() {
DispatchQueue.global(qos: .background).async {
DispatchQueue.main.async {
self.removeAllPendingAndDeliveredNotifications()
// create notifications for the next coming 12 days
for index in 0..<12 {
let newDate = Calendar.current.date(byAdding: .day, value: index, to: Date())!
let prayers = self.getPrayerDatetime(forDate: newDate)
// create notification for each prayer
for iterator in 0..<prayers.count {
// Skip sunrise
if iterator == 1 { continue }
// Skip the passed dates
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: prayers[iterator])
self.scheduleNotificationFor(prayerId: iterator, prayerTime: components, request: "\(index)\(iterator)")
}
}
}
}
}
/// Schedule a notification for a specific prayer
@objc private func scheduleNotificationFor(prayerId: Int, prayerTime: DateComponents, request: String) {
let notifContent = UNMutableNotificationContent()
// create the title
let title = NSLocalizedString("app_title", comment: "Prayer Times")
// create the prayer name
let prayerName = NSLocalizedString("prayer_" + String(prayerId), comment: "Prayer")
// set notification items
notifContent.title = title
notifContent.body = String.localizedStringWithFormat(NSLocalizedString("time_to_pray", comment: ""), prayerName)
notifContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "adhan.mp3"))
let notifTrigger = UNCalendarNotificationTrigger(dateMatching: prayerTime, repeats: false)
let notifRequest = UNNotificationRequest(identifier: title + request, content: notifContent, trigger: notifTrigger)
UNUserNotificationCenter.current().add(notifRequest, withCompletionHandler: nil)
}
/// This removes all current notifications before creating the new ones
func removeAllPendingAndDeliveredNotifications() {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}
这对我的 Prayer Times 应用程序来说效果很好。
我希望这会有所帮助;)