|
|
@@ -17,6 +17,7 @@
|
|
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
|
|
|
|
|
#import <react/config/ReactNativeConfig.h>
|
|
|
+#import <AVFoundation/AVFoundation.h>
|
|
|
|
|
|
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
|
|
|
@@ -26,9 +27,14 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
|
|
facebook::react::ContextContainer::Shared _contextContainer;
|
|
|
}
|
|
|
+
|
|
|
@end
|
|
|
#endif
|
|
|
|
|
|
+@interface AppDelegate()
|
|
|
+@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
|
|
|
+@end
|
|
|
+
|
|
|
@implementation AppDelegate
|
|
|
|
|
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
|
|
@@ -58,7 +64,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
|
|
|
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
|
|
|
|
-// UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
+ // UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
center.delegate = self;
|
|
|
|
|
|
|
|
|
@@ -90,23 +96,107 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
[super application:application didFinishLaunchingWithOptions:launchOptions];
|
|
|
|
|
|
[self clearAllDeliveredNotifications];
|
|
|
+
|
|
|
+ [self mixAudio];
|
|
|
return YES;
|
|
|
}
|
|
|
|
|
|
+- (void)mixAudio{
|
|
|
+
|
|
|
+ NSError *error = nil;
|
|
|
+ AVMutableComposition *composition = [AVMutableComposition composition];
|
|
|
+
|
|
|
+ // 创建音轨
|
|
|
+ AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
|
|
|
+
|
|
|
+ // 声音文件路径
|
|
|
+ NSString *soundFilePath1 = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"m4a"];
|
|
|
+ NSString *soundFilePath2 = [[NSBundle mainBundle] pathForResource:@"2" ofType:@"m4a"];
|
|
|
+
|
|
|
+ // 加载音频文件
|
|
|
+ AVAsset *asset1 = [AVAsset assetWithURL:[NSURL fileURLWithPath:soundFilePath1]];
|
|
|
+ AVAsset *asset2 = [AVAsset assetWithURL:[NSURL fileURLWithPath:soundFilePath2]];
|
|
|
+
|
|
|
+ // 获取音频文件的时长
|
|
|
+ CMTimeRange timeRange1 = CMTimeRangeMake(kCMTimeZero, asset1.duration);
|
|
|
+ CMTimeRange timeRange2 = CMTimeRangeMake(kCMTimeZero, asset2.duration);
|
|
|
+
|
|
|
+ // 将音频文件的音轨添加到合成的音轨中
|
|
|
+ [compositionTrack insertTimeRange:timeRange1 ofTrack:[[asset1 tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:kCMTimeZero error:&error];
|
|
|
+ if (error) {
|
|
|
+ // 错误处理
|
|
|
+ NSLog(@"Error composing sound: %@", [error localizedDescription]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 在第一段声音之后继续添加第二段声音
|
|
|
+ [compositionTrack insertTimeRange:timeRange2 ofTrack:[[asset2 tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:asset1.duration error:&error];
|
|
|
+ if (error) {
|
|
|
+ // 错误处理
|
|
|
+ NSLog(@"Error composing sound: %@", [error localizedDescription]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 输出文件路径
|
|
|
+ NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"outputSound.m4a"];
|
|
|
+ NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
|
+ BOOL fileExists = [fileManager fileExistsAtPath:outputFilePath];
|
|
|
+ if (fileExists) {
|
|
|
+ // 文件存在,删除文件
|
|
|
+ NSError *error;
|
|
|
+ BOOL success = [fileManager removeItemAtPath:outputFilePath error:&error];
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ NSLog(@"文件删除成功");
|
|
|
+ } else {
|
|
|
+ // 处理错误
|
|
|
+ NSLog(@"文件删除失败: %@", [error localizedDescription]);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ NSLog(@"文件不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ NSURL *outputURL = [NSURL fileURLWithPath:outputFilePath];
|
|
|
+
|
|
|
+ // 导出合成的音频
|
|
|
+ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
|
|
|
+ exportSession.outputURL = outputURL;
|
|
|
+ exportSession.outputFileType = AVFileTypeAppleM4A;
|
|
|
+
|
|
|
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
|
|
|
+ if (exportSession.status == AVAssetExportSessionStatusCompleted) {
|
|
|
+ // 导出成功,可以播放或者使用输出的文件
|
|
|
+ NSLog(@"Export success: %@", outputFilePath);
|
|
|
+ NSURL *fileURL = [NSURL fileURLWithPath:outputFilePath];
|
|
|
+ NSError *error;
|
|
|
+ self->_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
|
|
|
+
|
|
|
+ if (error) {
|
|
|
+ NSLog(@"Error creating audio player: %@", [error localizedDescription]);
|
|
|
+ } else {
|
|
|
+ [self->_audioPlayer play];
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 导出失败,处理错误
|
|
|
+ NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
|
|
|
+ }
|
|
|
+ }];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
|
|
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
|
|
|
}
|
|
|
- (void)registerForPushNotifications {
|
|
|
- UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
- [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
|
- if (granted) {
|
|
|
- dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
- [[UIApplication sharedApplication] registerForRemoteNotifications];
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 用户拒绝通知或发生错误
|
|
|
- }
|
|
|
- }];
|
|
|
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
+ [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
|
|
+ if (granted) {
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ [[UIApplication sharedApplication] registerForRemoteNotifications];
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 用户拒绝通知或发生错误
|
|
|
+ }
|
|
|
+ }];
|
|
|
}
|
|
|
|
|
|
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
|
|
@@ -130,7 +220,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
|
|
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
|
|
{
|
|
|
-// return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
|
|
+ // return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
|
|
#if DEBUG
|
|
|
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
|
|
#else
|
|
|
@@ -158,14 +248,14 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
}
|
|
|
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
|
|
- jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
|
|
+jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
|
|
{
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
|
|
- initParams:
|
|
|
- (const facebook::react::ObjCTurboModule::InitParams &)params
|
|
|
+initParams:
|
|
|
+(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
|
{
|
|
|
return nullptr;
|
|
|
}
|
|
|
@@ -181,7 +271,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
|
|
|
//注册 APNS 成功并上报 DeviceToken
|
|
|
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
|
|
-// [JPUSHService registerDeviceToken:deviceToken];
|
|
|
+ // [JPUSHService registerDeviceToken:deviceToken];
|
|
|
}
|
|
|
|
|
|
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
|
|
|
@@ -192,8 +282,8 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
- (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
|
|
// iOS 10 以下 Required
|
|
|
NSLog(@"iOS 7 APNS");
|
|
|
-// [JPUSHService handleRemoteNotification:userInfo];
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
+ // [JPUSHService handleRemoteNotification:userInfo];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
completionHandler(UIBackgroundFetchResultNewData);
|
|
|
}
|
|
|
|
|
|
@@ -203,13 +293,13 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
|
|
|
// Apns
|
|
|
NSLog(@"iOS 10 APNS 前台收到消息");
|
|
|
-// [JPUSHService handleRemoteNotification:userInfo];
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
+ // [JPUSHService handleRemoteNotification:userInfo];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
}
|
|
|
else {
|
|
|
// 本地通知 todo
|
|
|
NSLog(@"iOS 10 本地通知 前台收到消息");
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_ARRIVED_EVENT object:userInfo];
|
|
|
}
|
|
|
//需要执行这个方法,选择是否提醒用户,有 Badge、Sound、Alert 三种类型可以选择设置
|
|
|
completionHandler(UNNotificationPresentationOptionAlert);
|
|
|
@@ -228,7 +318,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
|
|
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler
|
|
|
{
|
|
|
- //应用程序在后台,用户通过点击本地推送、远程推送进入app时调用此方法
|
|
|
+ //应用程序在后台,用户通过点击本地推送、远程推送进入app时调用此方法
|
|
|
self.timestamp = [[NSDate date] timeIntervalSince1970]*1000;
|
|
|
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(postNotificationData:) userInfo:response repeats:YES];
|
|
|
}
|
|
|
@@ -239,59 +329,59 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
|
|
|
// Apns
|
|
|
NSLog(@"iOS 10 APNS 消息事件回调");
|
|
|
-// [JPUSHService handleRemoteNotification:userInfo];
|
|
|
-// // 保障应用被杀死状态下,用户点击推送消息,打开app后可以收到点击通知事件
|
|
|
-// [[RCTJPushEventQueue sharedInstance]._notificationQueue insertObject:userInfo atIndex:0];
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_OPENED_EVENT object:userInfo];
|
|
|
+ // [JPUSHService handleRemoteNotification:userInfo];
|
|
|
+ // // 保障应用被杀死状态下,用户点击推送消息,打开app后可以收到点击通知事件
|
|
|
+ // [[RCTJPushEventQueue sharedInstance]._notificationQueue insertObject:userInfo atIndex:0];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_OPENED_EVENT object:userInfo];
|
|
|
}
|
|
|
else {
|
|
|
// 本地通知
|
|
|
NSLog(@"iOS 10 本地通知 消息事件回调");
|
|
|
// 保障应用被杀死状态下,用户点击推送消息,打开app后可以收到点击通知事件
|
|
|
-// [[RCTJPushEventQueue sharedInstance]._localNotificationQueue insertObject:userInfo atIndex:0];
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_OPENED_EVENT object:userInfo];
|
|
|
+ // [[RCTJPushEventQueue sharedInstance]._localNotificationQueue insertObject:userInfo atIndex:0];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_OPENED_EVENT object:userInfo];
|
|
|
}
|
|
|
self.timestamp = [[NSDate date] timeIntervalSince1970]*1000;
|
|
|
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(postNotificationData:) userInfo:response repeats:YES];
|
|
|
-// NSString *categoryIdentifier = response.notification.request.content.categoryIdentifier;
|
|
|
-// [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"category_id":categoryIdentifier,@"action_id":response.actionIdentifier}];
|
|
|
+ // NSString *categoryIdentifier = response.notification.request.content.categoryIdentifier;
|
|
|
+ // [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"category_id":categoryIdentifier,@"action_id":response.actionIdentifier}];
|
|
|
|
|
|
// 处理用户交互
|
|
|
-// if ([response.actionIdentifier isEqualToString:@"ALLOW_ACTION"]) {
|
|
|
-// // 用户点击了"允许"按钮,发起网络请求
|
|
|
-// NSLog(@"User allow the request");
|
|
|
-//
|
|
|
-//// [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"name":@"1",@"value":@"2"}];
|
|
|
-//
|
|
|
-//// [self makeNetworkRequest];
|
|
|
-// } else if ([response.actionIdentifier isEqualToString:@"DENY_ACTION"]) {
|
|
|
-// // 用户点击了"拒绝"按钮
|
|
|
-// NSLog(@"User denied the request");
|
|
|
-//// [self makeNetworkRequest];
|
|
|
-// } else if ([response.actionIdentifier isEqualToString:@"START_TIMER_NOW"]){
|
|
|
-// if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// } else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_START"]){
|
|
|
-// if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// }
|
|
|
-// else if ([response.actionIdentifier isEqualToString:@"END_TIMER_NOW"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_END"]){
|
|
|
-//
|
|
|
-// }
|
|
|
-// else if ([response.actionIdentifier isEqualToString:@"SKIP"]){
|
|
|
-//
|
|
|
-// }
|
|
|
+ // if ([response.actionIdentifier isEqualToString:@"ALLOW_ACTION"]) {
|
|
|
+ // // 用户点击了"允许"按钮,发起网络请求
|
|
|
+ // NSLog(@"User allow the request");
|
|
|
+ //
|
|
|
+ //// [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"name":@"1",@"value":@"2"}];
|
|
|
+ //
|
|
|
+ //// [self makeNetworkRequest];
|
|
|
+ // } else if ([response.actionIdentifier isEqualToString:@"DENY_ACTION"]) {
|
|
|
+ // // 用户点击了"拒绝"按钮
|
|
|
+ // NSLog(@"User denied the request");
|
|
|
+ //// [self makeNetworkRequest];
|
|
|
+ // } else if ([response.actionIdentifier isEqualToString:@"START_TIMER_NOW"]){
|
|
|
+ // if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // } else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_START"]){
|
|
|
+ // if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // else if ([response.actionIdentifier isEqualToString:@"END_TIMER_NOW"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_END"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
+ // else if ([response.actionIdentifier isEqualToString:@"SKIP"]){
|
|
|
+ //
|
|
|
+ // }
|
|
|
// 系统要求执行这个方法
|
|
|
completionHandler();
|
|
|
}
|
|
|
@@ -333,25 +423,25 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
- (void)makeNetworkRequest{
|
|
|
//https://api.fast.dev.liveplus.fun/api/static-resource-urls
|
|
|
NSURL *url = [NSURL URLWithString:@"https://api.fast.dev.liveplus.fun/api/static-resource-urls"];
|
|
|
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
|
|
-
|
|
|
- NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
|
|
- if (error) {
|
|
|
- NSLog(@"Error: %@", error.localizedDescription);
|
|
|
- } else {
|
|
|
- // 处理返回的数据
|
|
|
- NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
|
- NSLog(@"Response: %@", json);
|
|
|
- }
|
|
|
- }];
|
|
|
-
|
|
|
- [dataTask resume];
|
|
|
+ NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
|
|
+
|
|
|
+ NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
|
|
+ if (error) {
|
|
|
+ NSLog(@"Error: %@", error.localizedDescription);
|
|
|
+ } else {
|
|
|
+ // 处理返回的数据
|
|
|
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
|
+ NSLog(@"Response: %@", json);
|
|
|
+ }
|
|
|
+ }];
|
|
|
+
|
|
|
+ [dataTask resume];
|
|
|
}
|
|
|
|
|
|
//自定义消息
|
|
|
- (void)networkDidReceiveMessage:(NSNotification *)notification {
|
|
|
NSDictionary * userInfo = [notification userInfo];
|
|
|
-// [[NSNotificationCenter defaultCenter] postNotificationName:J_CUSTOM_NOTIFICATION_EVENT object:userInfo];
|
|
|
+ // [[NSNotificationCenter defaultCenter] postNotificationName:J_CUSTOM_NOTIFICATION_EVENT object:userInfo];
|
|
|
}
|
|
|
|
|
|
- (void)demo{
|
|
|
@@ -360,47 +450,47 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
content.title = @"Daily Reminder";
|
|
|
content.body = @"It's 9:00 AM, time to start your day!";
|
|
|
content.sound = [UNNotificationSound defaultSound];
|
|
|
-
|
|
|
+
|
|
|
// 2. 设置触发条件 - 每天 9:00 AM
|
|
|
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
|
|
|
dateComponents.hour = 9;
|
|
|
dateComponents.minute = 0;
|
|
|
UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES];
|
|
|
-
|
|
|
+
|
|
|
// 3. 创建 notification request
|
|
|
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"DailyReminder" content:content trigger:trigger];
|
|
|
-
|
|
|
+
|
|
|
// 4. 添加 notification request 到通知中心
|
|
|
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
|
|
- if (error != nil) {
|
|
|
- NSLog(@"Error adding notification request: %@", error);
|
|
|
- }
|
|
|
+ if (error != nil) {
|
|
|
+ NSLog(@"Error adding notification request: %@", error);
|
|
|
+ }
|
|
|
}];
|
|
|
}
|
|
|
|
|
|
- (void)scheduleCalendarNotificationWithTitle:(NSString *)title body:(NSString *)body date:(NSDate *)date repeats:(BOOL)repeats identifier:(NSString *)identifier {
|
|
|
- NSLog(@"%s", __FUNCTION__);
|
|
|
- UNMutableNotificationContent *content = [UNMutableNotificationContent new];
|
|
|
- content.title = title;
|
|
|
- content.body = body;
|
|
|
-
|
|
|
- NSCalendar *calendar = NSCalendar.currentCalendar;
|
|
|
- NSDateComponents *components = [calendar components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date];
|
|
|
- UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:repeats];
|
|
|
-
|
|
|
- UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
|
|
|
- [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
|
|
- if (error) {
|
|
|
- NSLog(@"%@", error);
|
|
|
- }
|
|
|
- }];
|
|
|
+ NSLog(@"%s", __FUNCTION__);
|
|
|
+ UNMutableNotificationContent *content = [UNMutableNotificationContent new];
|
|
|
+ content.title = title;
|
|
|
+ content.body = body;
|
|
|
+
|
|
|
+ NSCalendar *calendar = NSCalendar.currentCalendar;
|
|
|
+ NSDateComponents *components = [calendar components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date];
|
|
|
+ UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:repeats];
|
|
|
+
|
|
|
+ UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
|
|
|
+ [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
|
|
+ if (error) {
|
|
|
+ NSLog(@"%@", error);
|
|
|
+ }
|
|
|
+ }];
|
|
|
}
|
|
|
|
|
|
|
|
|
- (void)clearAllDeliveredNotifications {
|
|
|
- UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
- [center removeAllDeliveredNotifications];
|
|
|
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
|
|
+ [center removeAllDeliveredNotifications];
|
|
|
}
|
|
|
|
|
|
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification{
|