This question already has an answer here:
APN custom notification sound issue
(1 answer)
Closed 9 years ago.
I have a sound file in the app bundle, i want to play that sound file when user will get push notification.
IS it possible in iOS if yes, then please suggest the way to achieve this.
Thanks,
To play this sound you must specify the filename of the sound in the notification payload. For example, lets say you've added a sound file named example.caf into your application, we can play this sound with the notification payload as below:
{
aps =
{
alert = "test example notification message";
sound = "example.caf";
};
}
Then the custom sound will play when your notification arrives.
Use this method in your app delegte class.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
NSLog(#"User Info : %#", [userInfo description]);
NSLog(#"User Info Alert Message : %#", [[userInfo objectForKey:#"aps"] objectForKey:#"alert"]);
NSString *messageString = [NSString stringWithFormat:#"%#", [[userInfo objectForKey:#"aps"] objectForKey:#"alert"]];
NSString *playSoundOnAlert = [NSString stringWithFormat:#"%#", [[userInfo objectForKey:#"aps"] objectForKey:#"sound"]];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#",[[NSBundle mainBundle] resourcePath],playSoundOnAlert]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
[audioPlayer play];
}
}
Related
I have a WKWebView and I am checking if audio is playing using
[[AVAudioSession sharedInstance] isOtherAudioPlaying]
I tried to get some information about it using :
[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo]
but it was nil.
Then I tried:
[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem]
and it was also nil.
How can I get URL of current playing audio?
You need to subscribe on notification :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemBecameCurrent:)
name:#"AVPlayerItemBecameCurrentNotification"
object:nil];
And then do this inside method playerItemBecameCurrent
-(void)playerItemBecameCurrent:(NSNotification*)notification {
AVPlayerItem *playerItem = [notification object];
if(playerItem == nil) return;
// Break down the AVPlayerItem to get to the path
AVURLAsset *asset = (AVURLAsset*)[playerItem asset];
NSURL *url = [asset URL];
NSString *path = [url absoluteString];}
As you can already detect when the audio is being played I think that the solution for you would be implementing decidePolicyForNavigationAction method from the WKNavigationDelegate.
You can combine the information provided by this delegate method that will be called whenever a link is clicked associated with the audio detection the you have already done.
I am creating a chat messenger app like whatsapp and trying to implement notification functionality similar to whatsapp. In whatsapp when you receive a notification it stores the data somewhere and when you turn off your wifi and go into the app the message is injected in the application. This mean whatsapp is somehow accessing the notification even when application is closed.
My Approach: I am receiving notification in background mode and saving that into a file. So if the user gets disconnected from the internet and goes into the app the messages are still injected on applicationWillAppear. This works perfect but when you forcefully close your app (Double clicking home and swiping the app up) it does not work. I have search almost everything and it says background fetch will not work if you forcefully close your application.
Then how whatsapp is doing it? What can be any other solution?
My Solution:
Turned on Background Modes for Background Fetch and remote notification from XCode. Next added following code inside application:willFinishLaunchingWithOptions:
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
Added this in AppDelegate.m file
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler{
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES);
if (0 < [paths count]) {
NSString *documentsDirPath = [paths objectAtIndex:0];
NSString *filePath = [documentsDirPath stringByAppendingPathComponent:#"notification.txt"];
NSString *content = [NSString stringWithFormat:#"%#", userInfo];
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
// Append text to the end of file
NSFileHandle *fileHandler = [NSFileHandle fileHandleForWritingAtPath:filePath];
[fileHandler seekToEndOfFile];
[fileHandler writeData:data];
[fileHandler closeFile];
} else {
// Create the file and write text to it.
[content writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
handler(UIBackgroundFetchResultNewData);
}
Update: I do have following flag in my notification
content-available: 1
Background pushes do not get delivered to a user-terminated app.
Unless it is a voip push, in that case the app will be started by the OS if necessary (but yes you can only make use of voip push if your app provides voip functionality to the user.)
In my current project I have a push notification. When I tap the app icon I want to get the received notification from the launch options object, but it always returns nil:
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
You can't detect that case, because application is not open using push notification (it has been open via app icon).
Try to open application by swiping push notification.
EDIT:
If you want to be invoked for push notification (via background fetch, when your application is not active) you need to ask your backend developer to set "content-available": 1 in push notification.
After that -application:didReceiveRemoteNotification:fetchCompletionHandler: will be invoked (when push-notification arrives), so you can save the payload into a file and then when application will be open, you can read the file and take actions.
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"#BACKGROUND FETCH CALLED: %#", userInfo);
// When we get a push, just writing it to file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"userInfo.plist"];
[userInfo writeToFile:filePath atomically:YES];
completionHandler(UIBackgroundFetchResultNewData);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Checking if application was launched by tapping icon, or push notification
if (!launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"userInfo.plist"];
[[NSFileManager defaultManager] removeItemAtPath:filePath
error:nil];
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:filePath];
if (info) {
// Launched by tapping icon
// ... your handling here
}
} else {
NSDictionary *info = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
// Launched with swiping
// ... your handling here
}
return YES;
}
Also don't forget to enable "Remote notifications" in "Background Modes"
When you launch the application from a PUSH NOTIFICATION ACTION, [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey] will return you the push notification payload (in dictionary format). When I say push notification action, it means either tapping the push notification from action center or from the push notification alert dialog (Depending on the device settings, push notification delivery mechanism varies).
If you launch the application by tapping the APP ICON, [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey] always returns nil. Because, it hasn't been launched from any kind of push notification.
I have searched, and I found UILocalNotification, but it works only when app is not active and i can not implement custom sound and snooze. Here is my code
UILocalNotification* alarm = [[UILocalNotification alloc] init];
alarm.fireDate = [myPicker date];
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.hasAction = YES;
alarm.alertAction = #"SNOOZE";
alarm.repeatInterval = 0;
alarm.soundName = #"Notification.aif";
alarm.alertBody = #"Alarm";
//[app scheduleLocalNotification:alarm];
[[UIApplication sharedApplication] scheduleLocalNotification:alarm];
Help is appreciated.
Your Approach is absolutely correct. but i thought you forgot some things about UILocalNotification .
read documents
A UILocalNotification object specifies a notification that an app can schedule for presentation at a specific date and time. The operating system is responsible for delivering local notifications at their scheduled times; the app does not have to be running for this to happen.
thanks, i have used UILocalNotification with my own sound name made another way to play it when app is active, in didReceiveLocalNotification
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"Received notification");
// Play a sound and show an alert only if the application is active, to avoid doubly notifiying the user.
if ([application applicationState] == UIApplicationStateActive) {
NSLog(#"active");
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"alarm2" ofType:#"wav"];
NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &_mySound);
AudioServicesPlaySystemSound(self.mySound);
}
}
in alarm ,notification works fine in background as follows:
UILocalNotification *notification1=[[UILocalNotification alloc]init];
notification1.fireDate=alramtime;
notification1.alertBody=#"Training Time";
notification1.repeatInterval=NSDayCalendarUnit;
notification1.soundName=#"Alarm.caf";
///////
previousnotif=[[NSUserDefaults standardUserDefaults]objectForKey:#"notif1"];
previous=[NSKeyedUnarchiver unarchiveObjectWithData:previousnotif];
NSLog(#"alarm %#",previous);
if (previous!= NULL) {
[[UIApplication sharedApplication]cancelLocalNotification:previous];
[[NSUserDefaults standardUserDefaults]removeObjectForKey:#"notif1"];
}
NSData *alarm1=[NSKeyedArchiver archivedDataWithRootObject:notification1];
[notifdefaults setObject:alarm1 forKey:#"notif1"];
/////////
[[UIApplication sharedApplication] scheduleLocalNotification:notification1];
NSLog(#"new alarm %#",notification1);
but when i modify it to play in foreground too as follows:..its not working..Only alert appears but no sound???
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"KNIP"
message:notification.alertBody
delegate:self cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[alert show];
}
#end
When i log soundfile etc properties of notification..they work fine...but no sound is there...
In foreground you have to provide alert view and play sound if it requires, the notification will just call applicationDidReceiveLocalNotification. You can play the sound using AVAudioPlayer
//Playing sound
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath],notification.soundName]];
AVAudioPlayer *newAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];
self.audioPlayer = newAudioPlayer;
self.audioPlayer.numberOfLoops = -1;
[self.audioPlayer play];
[newAudioPlayer release];
If the application is foremost and visible when the system delivers
the notification, no alert is shown, no icon is badged, and no sound
is played. However, the application:didReceiveLocalNotification: is
called if the application delegate implements it. The
UILocalNotification instance is passed into this method, and the
delegate can check its properties or access any custom data from the
userInfo dictionary.