RGoff
RGoff

Reputation: 117

AudioServicesPlayAlertSound not playing custom sound during application:didReceiveRemoteNotification:

I hope someone else has an insight into this problem...

I am trying to play a custom sound that is loaded into my app's resources. I created the sound using the afconvert tool, as recommended in the Converting audio to CAF format for playback on iPhone using OpenAL thread. The specific command executed to produce my custom sound file is:

afconvert ~/Desktop/moof.au ~/Desktop/moof.caf -d ima4 -f caff -v

When I run my app on a test device (iPhone 5, iOS 7.0; or iPhone 4, iOS 6.1.3) and send a push notification containing the name of my custom sound file, the device vibrates but no sound plays.

Here's where it gets a little weird... If I set a breakpoint at the start of the method that plays the sound, and single-step through the code, the device plays the custom sound and vibrates in response to the push notification!

Here's the code in question:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    // If the app is running and receives a remote notification, the app calls this method to process
    // the notification payload. Your implementation of this method should use the app state to take an
    // appropriate course of action.

    ALog(@"push data package: %@", userInfo);
    NSDictionary *payload = [userInfo objectForKey:@"aps"];
    NSNumber *badgeNumber = [payload objectForKey:@"badge"];
    NSString *soundName = [payload objectForKey:@"sound"];

    // app is in foreground
    if (application.applicationState == UIApplicationStateActive) {
        ALog(@"active");
        if (badgeNumber) {
            application.applicationIconBadgeNumber = [badgeNumber integerValue];
        }
        [self playPushNotificationAlertSound:soundName];
    }
    // app is in background
    else if (application.applicationState == UIApplicationStateBackground) {
        ALog(@"background");
        if (badgeNumber) {
            application.applicationIconBadgeNumber = [badgeNumber integerValue];
        }
        [self playPushNotificationAlertSound:soundName];
    }
    // app was launched from inactive state
    else if ((application.applicationState == UIApplicationStateInactive)) {
        ALog(@"inactive");
        if (badgeNumber) {
            application.applicationIconBadgeNumber = [badgeNumber integerValue];
        }
    }
}

- (void)playPushNotificationAlertSound:(NSString *)soundName
{
    ALog(@"soundName = '%@'", soundName);

    if (![soundName isEqualToString:@"default"]) {

        NSURL *soundURL = [[NSBundle mainBundle] URLForResource:soundName withExtension:@"caf"];

        if (soundURL) {
            CFURLRef soundFileURLRef = (__bridge CFURLRef)soundURL;
            SystemSoundID soundFileObject = 0;
            OSStatus status = AudioServicesCreateSystemSoundID(soundFileURLRef, &soundFileObject);
            if (status == kAudioServicesNoError) {
                AudioServicesPlayAlertSound(soundFileObject);
                AudioServicesDisposeSystemSoundID(soundFileObject);
            }
            ALog(@"soundFileURLRef = %@", soundFileURLRef);
            ALog(@"soundFileObject = %i, status = %i", (unsigned int)soundFileObject, (int)status);
        }
    }
}

And here's the console log without breakpoints active, and no custom sound plays:

2013-09-22 11:02:40.123 MyAppUnderDevelopment[2489:60b] push data package: {
    "_" = BlyZ4SOYEeOEc5DiugJ6IA;
    aps =     {
        alert = "Test #1";
        badge = 1;
        sound = moof;
    };
}
2013-09-22 11:02:40.124 MyAppUnderDevelopment[2489:60b] active
2013-09-22 11:02:40.136 MyAppUnderDevelopment[2489:60b] soundName = 'moof'
2013-09-22 11:02:40.138 MyAppUnderDevelopment[2489:60b] soundFileURLRef = file:///var/mobile/Applications/31785684-2EFA-4FEB-95F3-3A6B82B16A4A/MyAppUnderDevelopment.app/moof.caf
2013-09-22 11:02:40.139 MyAppUnderDevelopment[2489:60b] soundFileObject = 4100, status = 0

And the console log with breakpoint active, single-stepping, and the sound plays:

2013-09-22 11:03:29.891 MyAppUnderDevelopment[2489:60b] push data package: {
    "_" = "I_yGQCOYEeO515DiugJkgA";
    aps =     {
        alert = "Test #2";
        badge = 2;
        sound = moof;
    };
}
2013-09-22 11:03:29.892 MyAppUnderDevelopment[2489:60b] active
2013-09-22 11:03:33.752 MyAppUnderDevelopment[2489:60b] soundName = 'moof'
2013-09-22 11:03:40.757 MyAppUnderDevelopment[2489:60b] soundFileURLRef = file:///var/mobile/Applications/31785684-2EFA-4FEB-95F3-3A6B82B16A4A/MyAppUnderDevelopment.app/moof.caf
2013-09-22 11:03:45.356 MyAppUnderDevelopment[2489:60b] soundFileObject = 4101, status = 0

Any helpful ideas on what's going wrong?

Upvotes: 1

Views: 2229

Answers (1)

RGoff
RGoff

Reputation: 117

After preparing my question, I was running through my test sequence to ensure I had correctly recorded the steps I had taken. While single-stepping quickly through the code, I discovered that calling...

            AudioServicesPlayAlertSound(soundFileObject);
            AudioServicesDisposeSystemSoundID(soundFileObject);

...in this way caused the soundFileObject to be disposed before the asynchronous sound had a chance to play. The short-term solution was to turn the soundFileObject into a retained property, and use lazy instantiation to create it as required.

Hope this helps someone else who may be banging their head against a wall.

Upvotes: 1

Related Questions