1

I am seeing a crash in AppDelegate. I believe it is related to a song ending just as I go to make an in-app purchase or possibly the sound effect that I play when an in-app purchase completes (sort of a coin sound), as it happens occasionally but not frequently.

The crash log (below below) points to this in particular as the last thing called before the crash:

4   SpriteKit                       0x233882e3c -[SKSoundSource purgeCompletedBuffers] + 155

I handle stopping and starting audio with code as follows:

func applicationWillResignActive(_ application: UIApplication)
    {
        // Nil out the system clock variables
        if GameData.sharedInstance.isValidSystemClock != nil && GameData.sharedInstance.isValidSystemClock!
        {
            GameData.sharedInstance.isValidSystemClock = nil
        }
        GameData.sharedInstance.currentServerTime = nil

        // Capture the last time the user was in the app, so we can catch if they rewind their system clock while offline
        GameData.sharedInstance.timeOfLastAppLaunch = CFAbsoluteTimeGetCurrent()

        // Save game data to file
        GameData.sharedInstance.saveGameToFile()

        //schedule notifications for user to come back soon!
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scheduleNotificationFreeChest"), object: nil)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scheduleNotificationReminders"), object: nil)
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scheduleNotificationLivesFull"), object: nil)

        //pause background music if it's playing
        if AssetsSounds.sharedInstance.bgmTitlePlayer?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmTitlePlayer?.pause()
            needToPlayTitleMusic = true
        }

        //pause game music if it's playing
        if AssetsSounds.sharedInstance.bgmGamePlayer?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmGamePlayer?.pause()
            needToPlayGameMusic = true
        }

        if AssetsSounds.sharedInstance.bgmBonusLoop?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmBonusLoop?.pause()
            needToPlayBonusLoop = true
        }

        if AssetsSounds.sharedInstance.bgmBonusLeadIn?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmBonusLeadIn?.pause()
            needToPlayBonusLeadIn = true
        }

        if AssetsSounds.sharedInstance.bgmFiveMovesLeft?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmFiveMovesLeft?.pause()
            needToPlayFiveMovesLeft = true
        }

        if AssetsSounds.sharedInstance.bgmRainWithBirds?.isPlaying ?? false
        {
            AssetsSounds.sharedInstance.bgmRainWithBirds?.pause()
            needToPlayRainWithBirds = true
        }

    }

And, upon return from the interruption:

func applicationDidBecomeActive(_ application: UIApplication)
    {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

        // kill notifications scheduled since user is back
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()

        // Clears app badge number; we put a 1 when we send a notification
        UIApplication.shared.applicationIconBadgeNumber = 0

        if AVAudioSession.sharedInstance().secondaryAudioShouldBeSilencedHint
        {
            AssetsSounds.sharedInstance.bgmGamePlayer?.stop()
            AssetsSounds.sharedInstance.bgmGamePlayer = nil
            needToPlayGameMusic = false
            AssetsSounds.sharedInstance.bgmTitlePlayer?.stop()
            AssetsSounds.sharedInstance.bgmTitlePlayer = nil
            needToPlayTitleMusic = false
            AssetsSounds.sharedInstance.bgmBonusLeadIn?.stop()
            AssetsSounds.sharedInstance.bgmBonusLeadIn = nil
            needToPlayBonusLeadIn = false
            AssetsSounds.sharedInstance.bgmBonusLoop?.stop()
            AssetsSounds.sharedInstance.bgmBonusLoop = nil
            needToPlayBonusLoop = false
            AssetsSounds.sharedInstance.bgmFiveMovesLeft?.stop()
            AssetsSounds.sharedInstance.bgmFiveMovesLeft = nil
            needToPlayFiveMovesLeft = false
            AssetsSounds.sharedInstance.bgmRainWithBirds?.stop()
            AssetsSounds.sharedInstance.bgmRainWithBirds = nil
            needToPlayRainWithBirds = false
        }
        else if needToPlayGameMusic || needToPlayTitleMusic || needToPlayRainWithBirds || needToPlayBonusLoop || needToPlayBonusLeadIn || needToPlayFiveMovesLeft
        {
            //play background music if it needs to be played
            if needToPlayTitleMusic
            {
                AssetsSounds.sharedInstance.bgmTitlePlayer?.play()
                needToPlayTitleMusic = false
            }

            //play game music if it needs to be played
            if needToPlayGameMusic
            {
                AssetsSounds.sharedInstance.bgmGamePlayer?.play()
                needToPlayGameMusic = false
            }

            if needToPlayBonusLoop
            {
                AssetsSounds.sharedInstance.bgmBonusLoop?.play()
                needToPlayBonusLoop = false
            }

            if needToPlayBonusLeadIn
            {
                AssetsSounds.sharedInstance.bgmBonusLeadIn?.play()
                needToPlayBonusLeadIn = false
            }

            if needToPlayFiveMovesLeft
            {
                AssetsSounds.sharedInstance.bgmFiveMovesLeft?.play()
                needToPlayFiveMovesLeft = false
            }

            if needToPlayRainWithBirds
            {
                AssetsSounds.sharedInstance.bgmRainWithBirds?.play()
                needToPlayRainWithBirds = false
            }
        } // end else if
        else
        {
            if viewController.mapScreen != nil
            {
                AssetsSounds.sharedInstance.bgmTitlePlayer = AssetsSounds.sharedInstance.loadAudioPlayer(SongType.title1, loops: 0)
                AssetsSounds.sharedInstance.bgmTitlePlayer?.play()
            }

            else if viewController.scene != nil && viewController.gameState != .gameover && viewController.gameState != .lost && viewController.gameState != .won && viewController.level != nil && viewController.level.songType != nil
            {
                    AssetsSounds.sharedInstance.bgmGamePlayer = AssetsSounds.sharedInstance.loadAudioPlayer(viewController.level.songType!, loops: 0)
                    AssetsSounds.sharedInstance.bgmGamePlayer?.play()
                    viewController.scene.addMusicCredits()
            } // end else if
        }
    }

Have others seen similar crashes related to sound / interruptions? What is the proper way to handle this?

I have seen another post: Sprite Kit & playing sound leads to app termination that references this, but I am not sure if my issue is the same or if the answer here is valid.

Below is the symbolic crash log from my phone:

OS Version:          iPhone OS 12.3.1 (16F203)
Baseband Version:    3.60.01
Report Version:      104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Application Specific Information:
abort() called

Last Exception Backtrace:
0   CoreFoundation                  0x21cb4927c __exceptionPreprocess + 228
1   libobjc.A.dylib                 0x21bd239f8 objc_exception_throw + 55
2   CoreFoundation                  0x21cac2ce8 _CFThrowFormattedException + 111
3   CoreFoundation                  0x21ca34298 -[__NSArrayM removeObjectsInRange:] + 2235
4   SpriteKit                       0x233882e3c -[SKSoundSource purgeCompletedBuffers] + 155
5   SpriteKit                       0x2338830d4 -[SKSoundSource dealloc] + 47
6   CoreFoundation                  0x21caa9ed4 cow_cleanup + 111
7   CoreFoundation                  0x21ca334d4 -[__NSArrayM dealloc] + 67
8   SpriteKit                       0x233883434 SKCRendererRemoveCompletedSoundSources+ 836660 () + 331
9   SpriteKit                       0x2337f12d0 -[SKScene _update:] + 751
10  SpriteKit                       0x233816114 -[SKView _update:] + 835
11  SpriteKit                       0x2338117f4 __51-[SKView _vsyncRenderForTime:preRender:postRender:]_block_invoke.351 + 387
12  SpriteKit                       0x233810bf8 -[SKView _vsyncRenderForTime:preRender:postRender:] + 519
13  SpriteKit                       0x233813c54 __29-[SKView setUpRenderCallback]_block_invoke + 207
14  SpriteKit                       0x233856c18 -[SKDisplayLink _callbackForNextFrame:] + 163
15  QuartzCore                      0x220ed0f90 CA::Display::DisplayLink::dispatch_items+ 69520 (unsigned long long, unsigned long long, unsigned long long) + 635
16  QuartzCore                      0x220f9ab10 display_timer_callback+ 895760 (__CFMachPort*, void*, long, void*) + 271
17  CoreFoundation                  0x21cab4a8c __CFMachPortPerform + 187
18  CoreFoundation                  0x21cadb690 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 55
19  CoreFoundation                  0x21cadaddc __CFRunLoopDoSource1 + 439
20  CoreFoundation                  0x21cad5c00 __CFRunLoopRun + 2095
21  CoreFoundation                  0x21cad50b0 CFRunLoopRunSpecific + 435
22  GraphicsServices                0x21ecd579c GSEventRunModal + 103
23  UIKitCore                       0x249341978 UIApplicationMain + 211
24  Pandamonium!                    0x100bdd8ec main + 22764 (AppDelegate.swift:19)
25  libdyld.dylib                   0x21c59a8e0 start + 3


Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x000000021c6e60dc __pthread_kill + 8
1   libsystem_pthread.dylib         0x000000021c7639b0 pthread_kill$VARIANT$armv81 + 296
2   libsystem_c.dylib               0x000000021c63fea8 abort + 140
3   libc++abi.dylib                 0x000000021bd0c788 __cxa_bad_cast + 0
4   libc++abi.dylib                 0x000000021bd0c934 default_unexpected_handler+ 6452 () + 0
5   libobjc.A.dylib                 0x000000021bd23e00 _objc_terminate+ 24064 () + 124
6   libc++abi.dylib                 0x000000021bd18838 std::__terminate(void (*)+ 55352 ()) + 16
7   libc++abi.dylib                 0x000000021bd18434 __cxa_rethrow + 144
8   libobjc.A.dylib                 0x000000021bd23bc8 objc_exception_rethrow + 44
9   CoreFoundation                  0x000000021cad511c CFRunLoopRunSpecific + 544
10  GraphicsServices                0x000000021ecd579c GSEventRunModal + 104
11  UIKitCore                       0x0000000249341978 UIApplicationMain + 212
12  Pandamonium!                    0x0000000100bdd8ec main + 22764 (AppDelegate.swift:19)
13  libdyld.dylib                   0x000000021c59a8e0 start + 4

Thread 1 name:  com.apple.uikit.eventfetch-thread
Thread 1:
0   libsystem_kernel.dylib          0x000000021c6db0f4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x000000021c6da5a0 mach_msg + 72
2   CoreFoundation                  0x000000021cadaa10 __CFRunLoopServiceMachPort + 236
3   CoreFoundation                  0x000000021cad5920 __CFRunLoopRun + 1360
4   CoreFoundation                  0x000000021cad50b0 CFRunLoopRunSpecific + 436
5   Foundation                      0x000000021d4a2fac -[NSRunLoop+ 32684 (NSRunLoop) runMode:beforeDate:] + 300
6   Foundation                      0x000000021d4a2e3c -[NSRunLoop+ 32316 (NSRunLoop) runUntilDate:] + 96
7   UIKitCore                       0x0000000249427494 -[UIEventFetcher threadMain] + 136
8   Foundation                      0x000000021d5cf6a4 __NSThread__start__ + 984
9   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
10  libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
11  libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 2 name:  AVAudioSession Notify Thread
Thread 2:
0   libsystem_kernel.dylib          0x000000021c6db0f4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x000000021c6da5a0 mach_msg + 72
2   CoreFoundation                  0x000000021cadaa10 __CFRunLoopServiceMachPort + 236
3   CoreFoundation                  0x000000021cad5920 __CFRunLoopRun + 1360
4   CoreFoundation                  0x000000021cad50b0 CFRunLoopRunSpecific + 436
5   AVFAudio                        0x00000002229bd334 GenericRunLoopThread::Entry+ 574260 (void*) + 156
6   AVFAudio                        0x00000002229e7c60 CAPThread::Entry+ 748640 (CAPThread*) + 88
7   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
8   libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
9   libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 3 name:  com.apple.NSURLConnectionLoader
Thread 3:
0   libsystem_kernel.dylib          0x000000021c6db0f4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x000000021c6da5a0 mach_msg + 72
2   CoreFoundation                  0x000000021cadaa10 __CFRunLoopServiceMachPort + 236
3   CoreFoundation                  0x000000021cad5920 __CFRunLoopRun + 1360
4   CoreFoundation                  0x000000021cad50b0 CFRunLoopRunSpecific + 436
5   CFNetwork                       0x000000021d0ee74c -[__CoreSchedulingSetRunnable runForever] + 216
6   Foundation                      0x000000021d5cf6a4 __NSThread__start__ + 984
7   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
8   libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
9   libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 4 name:  com.apple.coreaudio.AQClient
Thread 4:
0   libsystem_kernel.dylib          0x000000021c6db0f4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x000000021c6da5a0 mach_msg + 72
2   CoreFoundation                  0x000000021cadaa10 __CFRunLoopServiceMachPort + 236
3   CoreFoundation                  0x000000021cad5920 __CFRunLoopRun + 1360
4   CoreFoundation                  0x000000021cad50b0 CFRunLoopRunSpecific + 436
5   AudioToolbox                    0x0000000220b527d8 GenericRunLoopThread::Entry+ 2299864 (void*) + 156
6   AudioToolbox                    0x0000000220de14f8 CAPThread::Entry+ 4982008 (CAPThread*) + 88
7   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
8   libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
9   libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 5 name:  com.squareup.SocketRocket.NetworkThread
Thread 5:
0   libsystem_kernel.dylib          0x000000021c6db0f4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x000000021c6da5a0 mach_msg + 72
2   CoreFoundation                  0x000000021cadaa10 __CFRunLoopServiceMachPort + 236
3   CoreFoundation                  0x000000021cad5920 __CFRunLoopRun + 1360
4   CoreFoundation                  0x000000021cad50b0 CFRunLoopRunSpecific + 436
5   Foundation                      0x000000021d4a2fac -[NSRunLoop+ 32684 (NSRunLoop) runMode:beforeDate:] + 300
6   Pandamonium!                    0x0000000100f08d4c -[_FSRRunLoopThread main] + 3345740 (FSRWebSocket.m:1843)
7   Foundation                      0x000000021d5cf6a4 __NSThread__start__ + 984
8   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
9   libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
10  libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 6 name:  com.apple.CFSocket.private
Thread 6:
0   libsystem_kernel.dylib          0x000000021c6e6328 __select + 8
1   CoreFoundation                  0x000000021cae36f4 __CFSocketManager + 620
2   libsystem_pthread.dylib         0x000000021c7682c0 _pthread_body + 128
3   libsystem_pthread.dylib         0x000000021c768220 _pthread_start + 44
4   libsystem_pthread.dylib         0x000000021c76bcdc thread_start + 4

Thread 7:
0   libsystem_pthread.dylib         0x000000021c76bcd0 start_wqthread + 0

Thread 8:
0   libsystem_pthread.dylib         0x000000021c76bcd0 start_wqthread + 0

Thread 9:
0   libsystem_pthread.dylib         0x000000021c76bcd0 start_wqthread + 0

Thread 10:
0   libsystem_pthread.dylib         0x000000021c76bcd0 start_wqthread + 0
Mike Pandolfini
  • 532
  • 4
  • 17
  • 1
    From the look of it, your SKAudioNode finished playing a sound, but the sound was dealloced prior to your buffer finishing, so it is attempting to release nothing. Not sure why you are nilling out and recreating your audio player, but you need all of your audio nodes to point to the new player you are creating – Knight0fDragon Jun 12 '19 at 19:52
  • 1
    I only nil out my audio player in the event that the player has secondary audio playing. This wasn't the case in my crash. I simply pause my game music on interruption. As for sound FX I am using SKAction.playSoundFileNamed for sound FX. Not sure why the interruption would deallocate anything -- I made an IAP (interruption), but this should have just paused my game music. Sorry -- I know there's a lot of code there. I have tried changing those .pause() statements to .stop() and I have also removed the sound effect I have that plays right before I attempt to performIAP(). Thoughts? – Mike Pandolfini Jun 12 '19 at 21:09
  • 1
    To add on: issue here is I cannot reproduce the crash no matter how hard I try. It happened one other time ~3 weeks ago, so it's not that common, but obviously the idea of having any kind of susceptibility in an IAP scenario is not ideal. – Mike Pandolfini Jun 13 '19 at 01:47
  • 1
    Switch to SKAudioNode maybe? – Knight0fDragon Jun 13 '19 at 01:48
  • I agree in retrospect, but we have hundreds of sound effects and this would be a big overhaul. Was hoping we could make playSoundFileNamed work (but does it work? Is this method just garbage? I have read a bit about it to know people don't view it favorably.). We have seen this crash 3 times in the past 3 days now. It seems like it doesn't occur unless player has been playing for some time (10-20 minutes). It may also be worse on lower-memory devices (have had it twice on iPhone 6S but only once on iPhone X -- small sample obviously). – Mike Pandolfini Jun 14 '19 at 00:56
  • 1
    Find and replace on something like that should be simple. It does work, you are just doing weird things that apple did not think about when testing – Knight0fDragon Jun 14 '19 at 01:00
  • 1
    Well, I'm not sure what I'm supposed to do other than what I've done. It appears that if I don't stop (pause in the code above, but I've changed this) music and then restart it upon returning to app music and FX will cut out due to interruptions -- FX still cuts out at times due to interruptions. Additionally we want to allow for a player to have their own audio in the background, which requires us to check for secondary audio and kill our own audio in the meantime. The delegate seemed a reasonable place to do this -- though Apple mentions using an observer, which did not work consistently. – Mike Pandolfini Jun 14 '19 at 03:02
  • 1
    But this all aside, I don't believe it's my AVAudioPlayer that is the issue here (given the stack trace shows SpriteKit as the culprit, not AVFoundation) -- it is sound effects being played via playSoundFileNamed, over which I have no control. So I think I agree with your assessment that I should switch to SKAudioNode, but I have substantial repetitious sounds I need to play in certain instances and this doesn't lend itself as nicely I don't think. – Mike Pandolfini Jun 14 '19 at 03:05
  • 1
    Why are you using a second player? If you play from the audio library, any in game music should cancel, unless you are using skaction play sound for music? That is not what it is made for – Knight0fDragon Jun 14 '19 at 03:08
  • 1
    I meant if a player plays from, say, the Music app (secondary audio), we stop our AVAudioPlayer to allow for that music to dominate our soundtrack. We solely use SKAction.playSoundFileNamed for <5 s FX. – Mike Pandolfini Jun 14 '19 at 03:35
  • 1
    I don’t remember ever having to do that much work. Last time I handled audio, I remember that iOS automatically preferred the external music source over the internal, and the only sounds we allowed through was sound effects. – Knight0fDragon Jun 14 '19 at 15:49
  • 1
    By default your music is soloAmbient (your audio dominates other audio sessions). You change to ambient to allow other audio to mix with your audio, but of course we don't want our music mixing, so we watch for secondary audio incoming with AppDelegate. Apple describes this in documentation. That said, something is funky here. I have traced the bug to being that my audio session fails to get interrupted after many sounds are played at once -- memory spikes in these sorts of events, which correlates with an impending crash when the app is interrupted and it attempts to purge the sound. – Mike Pandolfini Jun 14 '19 at 19:58
  • 1
    OK, I just looked at my code, I am just being dumb here. AVAudioSession is a singleton. Back to your issue with the playsoundwithfile, if you only use if 4 times, switch to SKAudioNode – Knight0fDragon Jun 14 '19 at 20:25
  • 1
    We are moving all FX to SKAudioNode. Issue we are having is avoiding lag with rapid fire stop / start of FX. I may broach in a new question; thanks for the help! – Mike Pandolfini Jun 16 '19 at 00:19

0 Answers0