筆者が開発しているiOSのアプリで、App Store Connect経由でクラッシュログが繰り返し送られてくるものの、再現方法も解決方法もずっと謎であった不具合が解決したのでメモとして残しておきます。
問題の内容
送られてくるクラッシュログは以下のようなものでした。
Last Exception Backtrace:
0 CoreFoundation 0x18d2d75b4 __exceptionPreprocess + 220 (NSException.m:199)
1 libobjc.A.dylib 0x1a12c742c objc_exception_throw + 60 (objc-exception.mm:565)
2 CoreFoundation 0x18d1d3aac +[NSException raise:format:] + 112 (NSException.m:155)
3 UIKitCore 0x19001b6d4 -[UIStackView insertArrangedSubview:atIndex:] + 184 (UIStackView.m:103)
4 UIKitCore 0x18f1723e4 -[_UIButtonBar _layoutBar] + 2576 (UIButtonBar.m:555)
5 UIKitCore 0x18f175b38 __42-[_UIButtonBarStackView updateConstraints]_block_invoke + 52 (UIButtonBar.m:1293)
6 UIKitCore 0x190112468 +[UIView(Animation) performWithoutAnimation:] + 104 (UIView.m:13927)
7 UIKitCore 0x18f175ad8 -[_UIButtonBarStackView updateConstraints] + 120 (UIButtonBar.m:1292)
8 UIKitCore 0x19003458c -[UIView(AdditionalLayoutSupport) _sendUpdateConstraintsIfNecessaryForSecondPass:] + 484 (NSLayoutConstraint_UIKitAdditions.m:0)
9 UIKitCore 0x190034a44 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededCollectingViews:forSecondPass:] + 948 (NSLayoutConstraint_UIKitAdditions.m:4409)
10 UIKitCore 0x190034924 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededCollectingViews:forSecondPass:] + 660 (NSLayoutConstraint_UIKitAdditions.m:4390)
11 UIKitCore 0x190034924 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededCollectingViews:forSecondPass:] + 660 (NSLayoutConstraint_UIKitAdditions.m:4390)
12 CoreAutoLayout 0x1a154c060 -[NSISEngine withBehaviors:performModifications:] + 88 (NSISEngine.m:1917)
13 UIKitCore 0x1900351f4 __100-[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededWithViewForVariableChangeNotifications:]_block_invoke + 116 (NSLayoutConstraint_UIKitAdditions.m:4455)
14 UIKitCore 0x190033b30 -[UIView(AdditionalLayoutSupport) _withUnsatisfiableConstraintsLoggingSuspendedIfEngineDelegateExists:] + 128 (NSLayoutConstraint_UIKitAdditions.m:4154)
15 UIKitCore 0x190034db4 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededWithViewForVariableChangeNotifications:] + 188 (NSLayoutConstraint_UIKitAdditions.m:4454)
16 UIKitCore 0x190035cac -[UIView(AdditionalLayoutSupport) _updateConstraintsAtEngineLevelIfNeededWithViewForVariableChangeNotifications:] + 436 (NSLayoutConstraint_UIKitAdditions.m:4707)
17 UIKitCore 0x19010c028 -[UIView _updateConstraintsAsNecessaryAndApplyLayoutFromEngine] + 404 (UIView.m:12304)
18 UIKitCore 0x18f2659f8 -[UIToolbar layoutSubviews] + 56 (UIToolbar.m:712)
19 UIKitCore 0x19011f7dc -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2500 (UIView.m:17460)
20 QuartzCore 0x190627464 -[CALayer layoutSublayers] + 296 (CALayer.mm:10129)
21 QuartzCore 0x190627920 CA::Layer::layout_if_needed(CA::Transaction*) + 524 (CALayer.mm:9996)
22 QuartzCore 0x19063bd48 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 144 (CALayer.mm:2478)
23 QuartzCore 0x1905833e4 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 416 (CAContextInternal.mm:2380)
24 QuartzCore 0x1905ae6dc CA::Transaction::commit() + 732 (CATransactionInternal.mm:449)
25 QuartzCore 0x1905afa3c CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 96 (CATransactionInternal.mm:925)
26 CoreFoundation 0x18d253454 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36 (CFRunLoop.c:1799)
27 CoreFoundation 0x18d24d868 __CFRunLoopDoObservers + 576 (CFRunLoop.c:1912)
28 CoreFoundation 0x18d24de18 __CFRunLoopRun + 1056 (CFRunLoop.c:2953)
29 CoreFoundation 0x18d24d4cc CFRunLoopRunSpecific + 600 (CFRunLoop.c:3242)
30 GraphicsServices 0x1a3c31820 GSEventRunModal + 164 (GSEvent.c:2259)
31 UIKitCore 0x18fbf2a28 -[UIApplication _run] + 1072 (UIApplication.m:3270)
32 UIKitCore 0x18fbf8104 UIApplicationMain + 168 (UIApplication.m:4739)
33 KifuBox 0x102e6c2b0 main + 68 (WaitingViewController.swift:12)
34 libdyld.dylib 0x18cf14e60 start + 4
厄介な点の1つとして、自分が直接書いたソースコードはスタック中にはなく、何をトリガに起きているのかわからないことがありました。
最終的にはUIStackView
のinsertArrangedSubview
で落ちており、スタックの途中には_UIButtonBarStackView
などが出ているので、多分UIBarButtonItem
に関連する何かだとは思うのですが、どこで問題が起きているのか全くわかりませんでした。
発生の傾向を見ると、iOS13.4以降でしか起きていないようで、それがiOS13.4以降でしか発生しない現象なのか、たまたま時期的な関係でそう見えているのかはよくわかりませんでした。
原因と解決方法
結論としては、Apple Developers Forumにあった以下の記事が当たりでした。
https://developer.apple.com/forums/thread/130038
上記記事ではUIAlertController
だそうですが、筆者のアプリでは画面下部のUIToolbar
にUIBarButtonItem
を追加する際に、同じオブジェクトが含まれていると、iOS13.4以降ではクラッシュするという問題でした。(iOS13.3以前では同じコードでも問題は発生しない)
self.setToolbarItems([filterButton, fixSpace1, dummyIcon, flexSpace1, filterStateButton,
flexSpace2, dummyIcon, fixSpace2, dummyIcon], animated: true)
上記のソースコードが問題で、ツールバーにボタンを並べる際のレイアウト調整のために、見えないダミーのボタン(dummyIcon
)を3つ追加していたのですが、その3つが同じオブジェクトであったためにクラッシュしていました。
以下のように3つ別のオブジェクトにすることで解決しました(オブジェクトの生成コード等は省略)
self.setToolbarItems([filterButton, fixSpace1, dummyIcon1, flexSpace1, filterStateButton,
flexSpace2, dummyIcon2, fixSpace2, dummyIcon3], animated: true)
手がかりがinsertArrangedSubview
くらいしかなくて難航したので、少しでもネット上で情報が見つかるように記事を書いてみました。