Abedalkareem / games_services

A Flutter plugin to support game center and google play games services.
123 stars 50 forks source link

loadAchievements iOS 14.5 error: Unexpectedly found nil while implicitly unwrapping an Optional value #124

Open egonbeermat opened 1 year ago

egonbeermat commented 1 year ago

Had a user reach out regarding my app crashing on iOS 14.5 shortly after launching it. After watching a video capture of the event (no crash logs or Crashlytics available) I asked them on a hunch to log out of Game Center and launch my app - and it didn't crash.

I have subsequently set up a simulator for iOS 14.5 and the issue is reproducible - it happens in func loadAchievements in Achievements.swift. This is what I get from running the app from XCode:

2023-10-13 22:54:21.842631-0500 Runner[63420:306356] flutter: I: Achievements: Trying to load Game Center achievements
:0: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
2023-10-13 22:54:22.546419-0500 Runner[63420:306119] :0: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

Running from Flutter in the simulator, I get a problem report when it crashes:

Problem Report ------------------------------------- Translated Report (Full Report Below) ------------------------------------- Incident Identifier: 68E94F7A-6581-49B7-8E1C-7615CC3F35F4 CrashReporter Key: EAF6B5D3-6C53-CB98-B7B4-89ABF33EF5D8 Hardware Model: MacBookAir10,1 Process: Runner [68070] Path: /Users/USER/Library/Developer/CoreSimulator/Devices/DA90F20D-3243-44EC-AD24-81A1974BF210/data/Containers/Bundle/Application/DFE33798-140A-4E55-BA12-5EE9F9DD318A/Runner.app/Runner Identifier: com.beermatsoftwarellc.dopewars Version: 2.1.5 (2154) Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd_sim [38485] Coalition: com.apple.CoreSimulator.SimDevice.DA90F20D-3243-44EC-AD24-81A1974BF210 [4803] Responsible Process: SimulatorTrampoline [38139] Date/Time: 2023-10-13 23:01:53.2904 -0500 Launch Time: 2023-10-13 23:01:47.4117 -0500 OS Version: macOS 13.6.1 (22G311) Release Type: User Report Version: 104 Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000001, 0x000000018e0223d4 Termination Reason: SIGNAL 5 Trace/BPT trap: 5 Terminating Process: exc handler [68070] Triggered by Thread: 0 Kernel Triage: VM - (arg = 0x0) pmap_enter retried due to resource shortage VM - (arg = 0x0) pmap_enter retried due to resource shortage VM - (arg = 0x0) pmap_enter retried due to resource shortage VM - (arg = 0x0) pmap_enter retried due to resource shortage VM - (arg = 0x0) pmap_enter retried due to resource shortage Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libswiftCore.dylib 0x18e0223d4 closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) + 380 1 libswiftCore.dylib 0x18e021b14 _assertionFailure(_:_:file:line:flags:) + 376 2 libswiftCore.dylib 0x18e022464 _diagnoseUnexpectedNilOptional(_filenameStart:_filenameLength:_filenameIsASCII:_line:_isImplicitUnwrap:) + 128 3 games_services 0x1051a85cc @objc completion handler block implementation for @escaping @callee_unowned @convention(block) (@unowned UIImage?, @unowned NSError?) -> () with result type UIImage + 156 4 GameCenterUI 0x1a1211994 __63-[GKAchievementDescription(UI) loadImageWithCompletionHandler:]_block_invoke + 72 5 GameCenterUICore 0x1b5a52dc4 __77+[UIImage(GKAdditions) _gkloadRemoteImageForURL:queue:withCompletionHandler:]_block_invoke_2 + 164 6 GameCenterFoundation 0x1ac248360 __99+[NSData(GKAdditions) _gkLoadRemoteImageDataForUrl:subdirectory:filename:queue:imageQueue:handler:]_block_invoke.106 + 64 7 GameCenterFoundation 0x1ac2687e8 __39-[GKDispatchGroup notifyOnQueue:block:]_block_invoke.49 + 28 8 libdispatch.dylib 0x180100580 _dispatch_call_block_and_release + 24 9 libdispatch.dylib 0x180101d44 _dispatch_client_callout + 16 10 libdispatch.dylib 0x18010f2f4 _dispatch_main_queue_callback_4CF + 956 11 CoreFoundation 0x180365d1c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 12 CoreFoundation 0x18036025c __CFRunLoopRun + 2448 13 CoreFoundation 0x18035f3bc CFRunLoopRunSpecific + 572 14 GraphicsServices 0x18afdd70c GSEventRunModal + 160 15 UIKitCore 0x1843f03d0 -[UIApplication _run] + 964 16 UIKitCore 0x1843f51ac UIApplicationMain + 112 17 Runner 0x1042882cc main + 64 (AppDelegate.swift:5) 18 dyld 0x1047e9f28 start + 2236

The culprit appears to be Achievements.swift line 48:

let imageData = try? await description.loadImage().pngData()

I see a handful of very similar crash reports in Crashlytics, all from iOS 14.x versions. This runs fine for me on iOS 15, 16 and 17 against the same set of achievements (all have images), and the issue only manifests in iOS 14.x for my users (and me in the 14.5 simulator).

After adding diagnostic prints during loading of my achievements, at first it seemed to randomly strike on different achievements but that may be due to the crash occurring before print statements flushed? After adding a short 50ms sleep inside the loop and before/after the highlighted line, it seems to be consistently occurring for two of my achievements. They both have an image defined in Game Center and, as stated earlier, don't seem to cause issues on iOS > 14. If I skip the line above for these 2 achievements, the function completes without error.

Thoughts? I'd like to resolve this but I'm not a Swifty and although iOS 14.x is relatively rare in the wild, it seems like it is hitting several of my users, but I can't seem to Google how to effectively catch the error and move on, perhaps setting the achievement to the default image...

egonbeermat commented 1 year ago

Update: belatedly, I've noticed that the iOS Game Center view also fails to load/show images for some of my achievements, whilst being fine for others. This is perplexing as a) it used to be 100% fine, b) images are mandatory for the localization, c) App Store Connect rejects uploads of invalid image formats, so this may be something on the Apple side. I assume that the loadImage() on iOS 14 blows up due to images being mandatory and thus not having sufficient error trapping in place.

This started happening the day after I launched an update which contained 6 new achievements in the release, though that could be coincidence. The issue occurs on some achievements from both the originally released set and the new set.

Abedalkareem commented 1 year ago

Hi @egonbeermat Interesting, looking at the code does not seem anything can cause a crash there,

let imageData = try? await description.loadImage().pngData()

we are using try?, which means if it fails to load the image it will be null, and after that, we are handling imageData safely.

I'll try one thing,

let image = try? await description.loadImage()
let imageData = image?.pngData()

I'll try to first get the image, and then get the data. I have a feeling that description.loadImage() forced unwrapped the image in the first case for some reason.

egonbeermat commented 1 year ago

Hi @Abedalkareem - Thanks for the response, and thanks again for this package!! I looked into this issue some more, as Game Center itself was not showing some achievement images as well. I think ultimately the issue is internal to loadImage itself, on iOS 14 it was crashing immediately with the missing images, but on later iOS versions did not. I'm assuming the code in iOS 14 had no protection for the image not being returned (as its mandatory) and thus assumed success and tried an operation that makes it just quit. I could see in the console logs for Game Center references to loadRemoteImageForURL failing, with the same number of references as missing achievement images.

I had tried what you suggested, too, to try to isolate it more, but no matter how I attempted to work around this, it would hard fault - iOS 14 seems to take the view that if you have the description, you should have the image available :)

However, I pushed out a new release independently of this issue (and with nothing new in the achievements area, I released those a few releases ago), and the problem resolved itself shortly after - no missing images in Game Center, no crashes inside the app calling that code. This left me unable to test and experiment more. I assume something around the internal state of my app and associated Game Center objects was corrupted on the Apple side, and the new release refreshed this.

===========

FYI - My users are seeing a couple of other issues with iOS 14.x with regards to Game Center too, at least some earlier 14.x releases. You can probably try this yourself on an iOS 14.3 simulator, but your getPlayerScore method on iOS 14.3 does not return the score, but it works fine on iOS 14.5 and higher. Further investigation seems it does not work as documented by Apple on iOS 14.2, iOS 14.3 (levels I've tested)

Specifically, for this in getPlayerScore:

let response = try await leaderboard.first?.loadEntries(for: [currentPlayer], timeScope: .allTime)
let (localPlayerEntry, _) = response ?? (nil, nil)

localPlayerEntry is not returned on iOS 14.3 but is on iOS 14.5 and higher. If you expand the response to:

let (localPlayerEntry, scores) = response ?? (nil, nil)

then on iOS 14.3, localPlayerEntry is not populated, but the scores array does contain the player score object. This all works as documented on iOS 14.5 and higher, though.

On iOS 14.3, this:

let response = try await leaderboard.first?.loadEntries(for: [currentPlayer], timeScope: .allTime)
let (localPlayerEntry, scores) = response ?? (nil, nil)
print("localPlayer: ", localPlayerEntry?.rank, ", ", localPlayerEntry?.formattedScore)
print("scores[0]  : ", scores![0].rank, ", ", scores![0].formattedScore)

results in:

localPlayer:  Optional(0) ,  Optional("")
scores[0]  :  16 ,  94,920,731

But flipping to an iOS 16.4 simulator for the same logged in user results in:


localPlayer:  Optional(16) ,  Optional("94,920,731")
scores[0]  :  16 ,  94,920,731

While iOS 14.x isn't widely used anymore, the only users I've had actually reach out to me about crashes are iOS 14 users :) They typically have a lower-end iPhone model that struggle with performance, and don't want to upgrade to higher iOS versions and risk further performance degradation.

theLee3 commented 9 months ago

Ignore that mention. This issue is unrelated.

Seems like it should be a similar fix on the Game Center side though.

theLee3 commented 1 week ago

@egonbeermat I was working on closing some issues but am feasibly unable to test on iOS 14.3 right now. Any idea where this issue stands? I believe I can roll out a solution if you have a way to test it.

egonbeermat commented 3 days ago

@theLee3 Sorry, was away from this for a couple of weeks. Let me look at it again, and report back....