8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftAdvent Calendar 2021

Day 7

iOS13 SwiftUIバグ集

Posted at

TL;DR

悪いこと言わんからiOS13でSwiftUI使うのはやめとけ

はじめに

この記事は仕事でSwiftUIのアプリを実装していた際に遭遇したiOS13のバグの記録です。
iOS15もリリースされ今更iOS13を気にする場合も減っていくかと思いますが、記念碑としてまとめました。

またここに書いたものは自分が実際に出会ったバグですが、再現コード等をインターネットから引用している箇所があります。

検証環境

  • Macbook Pro 13 inch Late 2013
  • macOS Big Sur 11.5.2
  • Xcode 13.1
  • iOS Simulator

サンプルコード

trickart/iOS13SwiftUIBugs

明らかなバグ

iPadでActionSheetを表示しようとするとクラッシュする(13.0-13.3)

以下のようなログが出力されてクラッシュしてしまう。

2021-12-07 02:10:50.177471+0900 iOS13SwiftUIBugs[18960:1110810] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController (<UIAlertController: 0x7fbd8d840e00>) of style UIAlertControllerStyleActionSheet from _TtGC7SwiftUI19UIHostingControllerV16iOS13SwiftUIBugs11ContentView_ (<_TtGC7SwiftUI19UIHostingControllerV16iOS13SwiftUIBugs11ContentView_: 0x7fbd8bd023a0>). The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem.  If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00000001120dcbde __exceptionPreprocess + 350
	1   libobjc.A.dylib                     0x000000010e78eb20 objc_exception_throw + 48
	2   UIKitCore                           0x000000011b9d077e __66-[UIPopoverPresentationController presentationTransitionWillBegin]_block_invoke + 0
	3   UIKitCore                           0x000000011b9dac53 __71-[UIPresentationController _initViewHierarchyForPresentationSuperview:]_block_invoke + 2604
	4   UIKitCore                           0x000000011b9d849b __56-[UIPresentationController runTransitionForCurrentState]_block_invoke.452 + 536
	5   UIKitCore                           0x000000011c13485a _runAfterCACommitDeferredBlocks + 352
	6   UIKitCore                           0x000000011c12563c _cleanUpAfterCAFlushAndRunDeferredBlocks + 248
	7   UIKitCore                           0x000000011c154c6e _afterCACommitHandler + 85
	8   CoreFoundation                      0x000000011203eeb7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
	9   CoreFoundation                      0x000000011203994e __CFRunLoopDoObservers + 430
	10  CoreFoundation                      0x0000000112039fca __CFRunLoopRun + 1514
	11  CoreFoundation                      0x00000001120396b6 CFRunLoopRunSpecific + 438
	12  GraphicsServices                    0x0000000113c7abb0 GSEventRunModal + 65
	13  UIKitCore                           0x000000011c12ba67 UIApplicationMain + 1621
	14  libswiftUIKit.dylib                 0x000000011159335b $s5UIKit17UIApplicationMainys5Int32VAD_SpySpys4Int8VGGSgSSSgAJtF + 155
	15  iOS13SwiftUIBugs                    0x000000010de22208 $sSo21UIApplicationDelegateP5UIKitE4mainyyFZ + 104
	16  iOS13SwiftUIBugs                    0x000000010de2218c $s16iOS13SwiftUIBugs11AppDelegateC5$mainyyFZ + 28
	17  iOS13SwiftUIBugs                    0x000000010de22288 main + 24
	18  libdyld.dylib                       0x0000000112f8ecf5 start + 1
	19  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController (<UIAlertController: 0x7fbd8d840e00>) of style UIAlertControllerStyleActionSheet from _TtGC7SwiftUI19UIHostingControllerV16iOS13SwiftUIBugs11ContentView_ (<_TtGC7SwiftUI19UIHostingControllerV16iOS13SwiftUIBugs11ContentView_: 0x7fbd8bd023a0>). The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem.  If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'
terminating with uncaught exception of type NSException
CoreSimulator 776.4 - Device: iPad mini 5th(13.0) (045FAB2E-2DDF-49FF-93DD-9F1611540BB5) - Runtime: iOS 13.0 (17A577) - DeviceType: iPad mini (5th generation)

ログを見た限りではUIKitでActionSheetを表示する際にsourceViewを指定し忘れたときと同じ現象が起きていると推察される。

[Swift]iPadのActionSheet表示でクラッシュする問題 | RE:ENGINES

(正直UIKitのこの仕様も微妙だし、Apple自身がSwiftUIを実装する中でこの罠にハマってしまっている)

回避方法

Method Swizzlingを用いてUIAlertControllerのイニシャライザを書き換え強制的にAlertとして表示させる

参考: [SwiftUI]iPadでActionSheetが使えないという事実をねじ伏せる - Qiita

GeometryProxyのデータにアクセスするとクラッシュする場合がある(13.0-13.2)

以下のようなログを出力してクラッシュしてしまう。

2021-12-07 02:25:28.798110+0900 iOS13SwiftUIBugs[19408:1164577] precondition failure: attribute failed to set an initial value: 86
CoreSimulator 776.4 - Device: 8(13.0) (CEA2DCAB-4288-4ECE-AF94-F31CCBDB2DBF) - Runtime: iOS 13.0 (17A577) - DeviceType: iPhone 8
AttributeGraph precondition failure: attribute failed to set an initial value: 86.

回避法

おそらくなし…
AnyViewを挟むと回避できるといった情報もあるが、Viewがうまく表示されなかったりする…

How to track down "precondition failure: attribute failed to set an initial value: ???" errors? | Apple Developer Forums

Alert表示中に別のAlertを表示してしまうとクラッシュする(13.0-13.2, 13.4-13.7)

backtraceでSwiftUI.AlertBridge.preferencesDidChange()などが表示される。

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x000000010a799bc2 SwiftUI`SwiftUI.AlertBridge.preferencesDidChange(SwiftUI.PreferenceList) -> () + 2866
    frame #1: 0x000000010a265b73 SwiftUI`SwiftUI._UIHostingView.preferencesDidChange() -> () + 387
    frame #2: 0x000000010a36125a SwiftUI`SwiftUI.ViewGraph.updateOutputs(at: SwiftUI.Time) -> () + 234
    frame #3: 0x000000010a6b76ea SwiftUI`closure #1 () -> () in closure #1 () -> () in SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool) -> () + 122
    frame #4: 0x000000010a6b746c SwiftUI`closure #1 () -> () in SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool) -> () + 636
    frame #5: 0x000000010a6b4d47 SwiftUI`SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool) -> () + 439
    frame #6: 0x000000010a84f472 SwiftUI`SwiftUI._UIHostingView.layoutSubviews() -> () + 226
    frame #7: 0x000000010a84f495 SwiftUI`@objc SwiftUI._UIHostingView.layoutSubviews() -> () + 21
    frame #8: 0x0000000116bf3722 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478
    frame #9: 0x000000010fb73ef9 QuartzCore`-[CALayer layoutSublayers] + 255
    frame #10: 0x000000010fb788ff QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 517
    frame #11: 0x000000010fb84fe4 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 80
    frame #12: 0x000000010facd4a8 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double) + 324
    frame #13: 0x000000010fb02ab3 QuartzCore`CA::Transaction::commit() + 643
    frame #14: 0x0000000116748cb9 UIKitCore`_afterCACommitHandler + 160
    frame #15: 0x000000010c7cfeb7 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #16: 0x000000010c7ca94e CoreFoundation`__CFRunLoopDoObservers + 430
    frame #17: 0x000000010c7cafca CoreFoundation`__CFRunLoopRun + 1514
    frame #18: 0x000000010c7ca6b6 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #19: 0x000000010e38bbb0 GraphicsServices`GSEventRunModal + 65
    frame #20: 0x000000011671fa67 UIKitCore`UIApplicationMain + 1621
    frame #21: 0x000000010990835b libswiftUIKit.dylib`UIKit.UIApplicationMain(Swift.Int32, Swift.Optional<Swift.UnsafeMutablePointer<Swift.UnsafeMutablePointer<Swift.Int8>>>, Swift.Optional<Swift.String>, Swift.Optional<Swift.String>) -> Swift.Int32 + 155
    frame #22: 0x0000000108418208 iOS13SwiftUIBugs`static UIApplicationDelegate.main() at <compiler-generated>:0
  * frame #23: 0x000000010841818c iOS13SwiftUIBugs`static AppDelegate.$main(self=iOS13SwiftUIBugs.AppDelegate) at AppDelegate.swift:10:1
    frame #24: 0x0000000108418288 iOS13SwiftUIBugs`main at <compiler-generated>:0
    frame #25: 0x000000010d69fcf5 libdyld.dylib`start + 1
    frame #26: 0x000000010d69fcf5 libdyld.dylib`start + 1

SwiftUI crash in AlertBridge.preferencesDidChange(_:) | Apple Developer Forums

回避方法

同時にAlertを表示してしまわないように頑張る…
(表示しようとしたときにrootViewControllerから辿ってpresentedViewControllerがUIAlertControllerならdismissするなど)

バグらしきもの

以降は仕様の可能性があるものの、のちのバージョンで挙動が変わったことからバグとも考えられるものです。

ButtonのlabelにImageを設定すると水色に塗りつぶされる(13.0-13.2, 13.4-13.7)

button_render_mode.png

対処法

.renderingMode(.original) をつける

iPadでPopoverのサイズが固定になってしまう(13.0-13.3)

13.0 13.4
popover_13.0.png popover_13.4.png

対処法

おそらくなし…

おわりに

SwiftUIは宣言的にUIを記述でき優れた点も多いですが、課題も未だ多いフレームワークです。
その課題を気にしなくていいか、対処できる場合に使うことをおすすめします。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?