Skip to content

[TestFlight] crash · build 53 · SKStoreProductViewController throws on teardown when tapping a friend app #32

Description

@Kemmey

TestFlight feedback — crash · 2026-03-09T17:18:53.466Z

Tester comment

Tapped a friend

Build context

  • Build: 53 (VALID)
  • App version: 2.0.1
  • Commit: (unresolved — build wasn't from Xcode Cloud)

Device

  • Model: iPhone18_2
  • OS: 26.4
  • Architecture: arm64e
  • Locale: en-DK
  • Time zone: Europe/Copenhagen
  • Connection: WIFI
  • Battery: 80%

Crash signature

  • Exception Type: EXC_CRASH (SIGABRT)
  • Exception Codes: 0x0000000000000000, 0x0000000000000000
  • Termination Reason: SIGNAL 6 Abort trap: 6
  • Triggered by Thread: 0 (main)

The crashing thread is only the abort machinery — an uncaught Objective-C exception unwinding out of the main runloop:

Thread 0 Crashed:
0   libsystem_kernel.dylib  __pthread_kill + 8
1   libsystem_pthread.dylib pthread_kill + 268 (pthread.c:1721)
2   libsystem_c.dylib       abort + 148 (abort.c:122)
3   libc++abi.dylib         __abort_message + 132 (abort_message.cpp:66)
4   libc++abi.dylib         demangling_terminate_handler() + 296 (cxa_default_handlers.cpp:77)
5   libobjc.A.dylib         _objc_terminate() + 156 (objc-exception.mm:496)
6   libc++abi.dylib         std::__terminate(void (*)()) + 16 (cxa_handlers.cpp:59)
7   libc++abi.dylib         std::terminate() + 108 (cxa_handlers.cpp:88)
8   CoreFoundation          __CFRunLoopPerCalloutARPEnd + 256 (CFRunLoop.c:769)
9   CoreFoundation          __CFRunLoopRun + 1976 (CFRunLoop.c:3179)
10  CoreFoundation          _CFRunLoopRunSpecificWithOptions + 532 (CFRunLoop.c:3462)

The real call site is in the Last Exception Backtrace, and it is fully symbolicated:

Last Exception Backtrace:
0   CoreFoundation  __exceptionPreprocess + 164 (NSException.m:249)
1   libobjc.A.dylib objc_exception_throw + 88 (objc-exception.mm:356)
2   CoreFoundation  +[NSException raise:format:] + 128 (NSException.m:0)
3   StoreKit        -[SKStoreProductViewController _throwUnsupportedPresentationException] + 72 (SKStoreProductViewController.m:1187)
4   StoreKit        -[SKStoreProductViewController _configureForFullScreenPresentationOrThrowException] + 80 (SKStoreProductViewController.m:1180)
5   StoreKit        -[SKStoreProductViewController willMoveToParentViewController:] + 40 (SKStoreProductViewController.m:520)
6   UIKitCore       UICorePlatformViewHost.removeFromSuperview() + 124 (UICorePlatformViewHost.swift:246)
7   UIKitCore       @objc UICorePlatformViewHost.removeFromSuperview() + 36 (<compiler-generated>:0)
8   UIKitCore       -[UIView dealloc] + 348 (UIView.m:5308)
9   SwiftUI         _UIHostingView.__deallocating_deinit + 420 (UIHostingView.swift:599)
10  SwiftUI         @objc _UIHostingView.__deallocating_deinit + 28 (<compiler-generated>:0)
11  libobjc.A.dylib AutoreleasePoolPage::releaseUntil(objc_object**) + 204 (NSObject.mm:1017)
12  libobjc.A.dylib objc_autoreleasePoolPop + 244 (NSObject.mm:2275)
13  CoreFoundation  _CFAutoreleasePoolPop + 32 (NSObject.m:859)
14  CoreFoundation  __CFRunLoopPerCalloutARPEnd + 48 (CFRunLoop.c:766)

Analysis

The thrown exception is StoreKit's own, and it names its own reason: -[SKStoreProductViewController _throwUnsupportedPresentationException], reached from _configureForFullScreenPresentationOrThrowException. SKStoreProductViewController refuses to be anything but a full-screen modal — it must not be embedded as a child view controller or re-parented — and it enforces that by raising NSException from willMoveToParentViewController:.

Read the frames below the throw and the timing is the whole story:

_UIHostingView.__deallocating_deinit-[UIView dealloc]UICorePlatformViewHost.removeFromSuperview()willMoveToParentViewController:

The store controller is being torn down, not presented, when it throws. A SwiftUI hosting view is deallocating; UIKit's UICorePlatformViewHost (SwiftUI's UIKit view host — the thing that exists when a UIKit view/controller is embedded through a UIViewRepresentable / UIViewControllerRepresentable, not when a controller is present(_:animated:)-ed) tears down its hierarchy and re-parents the store controller to nil. StoreKit sees an unsupported presentation and throws.

That the throw escapes a dealloc inside an autorelease-pool drain (objc_autoreleasePoolPop__CFRunLoopPerCalloutARPEnd) is why there is nothing to catch it and why the crashing thread carries no app frame at all: the exception surfaces at the runloop's pool boundary, far from any code that could have handled it. It is unconditionally fatal.

Where this is in the source

AuthAppForTesla/View/About/AboutViewFriend.swift is the only place in the repo that touches SKStoreProductViewController (lines 49–64), and it is the "friend apps" grid on the About tab — which matches every tester comment on this cluster. Its history is directly relevant:

  • 47096fc (8 Mar 2026, "Fix friend app crash and login black screen") — removed SKStoreProductViewController entirely, with the commit message "crashes on dismiss in SwiftUI sheet", replacing it with openURL to apps.apple.com. That is this exact bug, already diagnosed once.
  • ae4086e (8 Mar 2026, 43 seconds later, "Restore in-app App Store overlay for friend apps") — put it back, now presented imperatively via topVC.present(storeVC, animated: true), on the grounds that this "resolves the crash while keeping the in-app product page experience".

The UICorePlatformViewHost frame says the build that crashed still had the store controller living inside a SwiftUI-hosted view hierarchy, i.e. the pre-47096fc StoreProductView representable-in-a-sheet. TestFlight did not record a commit for these builds (they were not from Xcode Cloud) and the build number is not in project.pbxproj, so the mapping from build 53/56 to a commit cannot be proven from this repo — but the stack shape is the pre-ae4086e code, not the current one.

That does not make the current code safe. The presentStoreProduct(appID:) that is on main today still has two problems that _configureForFullScreenPresentationOrThrowException can fire on:

  1. It walks presentedViewController to the topmost controller and presents there. In a SwiftUI app that top controller is typically a UIHostingController owned by a .sheet; when SwiftUI tears that sheet down it can deallocate the host out from under the store controller — the same ownership problem, reached by a different route.
  2. topVC.present(storeVC, animated: true) uses the ambient modalPresentationStyle. Nothing sets .fullScreen, so an inherited .formSheet/.popover (or an iPad presentation context) is exactly the "unsupported presentation" the assertion rejects.

Suggested fix

  • Present from the window scene's root view controller, not the topmost presented one.
  • Set storeVC.modalPresentationStyle = .fullScreen explicitly before presenting.
  • Set storeVC.delegate and dismiss through productViewControllerDidFinish(_:), holding a strong reference for its lifetime, so SwiftUI never owns its teardown.
  • Or take 47096fc's route and just openURL the apps.apple.com link — the in-app product page is a nicety, and this bug has now cost four crash reports.

Related

All four AuthAppForTesla TestFlight reports in this batch are the same defect — the friend-app SKStoreProductViewController presentation in AboutViewFriend.swift. The three build-53 reports have byte-identical exception paths; the build-56 report has no log but says "Still crashes".

This report ("Tapped a friend") is the first of the four and names the trigger most clearly: tapping a friend app in the About tab's friend grid.


Written by Claude (Opus 5) working in Kim's session — posted under his account because that is the only token available. Anything in this tracker without this footer was written by Kim himself.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcrashUser-visible crashmacMac Catalyst buildtestflightCame from TestFlight beta feedback

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions