Convenience init for UITableViewController crashes on iOS 15

Using a convenience init for a UITableViewController in a freshly created Xcode project with storyboards crashes on iOS 15, but works fine on iOS 14. The error message:

'UIViewController is missing its initial trait collection populated during initialization. This is a serious bug, likely caused by accessing properties or methods on the view controller before calling a UIViewController initializer.

How to reproduce

  • Open Xcode 13
  • Create a new project with Storyboards
  • Select iOS 14.0 for deployment info
  • Create an empty UITableViewController
class CustomTableViewController : UITableViewController {
 
  var data:String?
   
  convenience init() {
    self.init(data: "test")
  }
   
  // Adding @objc fixes the crash
  init(data: String) {
    self.data = data
    super.init(style: .plain)
  }
     
  required init?(coder: NSCoder) {
    super.init(coder: coder)
  }
}
  • Add the following to the main ViewController
class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
     
    _ = CustomTableViewController()
  }

}
  • Run project on iOS 15.0 simulator

The code works fine on an iOS 14.3 simulator. Prefixing init(data: String) with @objc fixes the problem.

Am I doing something wrong? Is this a bug in UIKit on iOS 15?

Detailed crash log

2021-10-01 10:38:40.146584+0200 CustomViewControllerInit[62559:627294] *** Assertion failure in UITraitCollection *UIViewControllerMissingInitialTraitCollection(UIViewController *__strong)(), UIViewController.m:2434
2021-10-01 10:38:40.155555+0200 CustomViewControllerInit[62559:627294] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIViewController is missing its initial trait collection populated during initialization. This is a serious bug, likely caused by accessing properties or methods on the view controller before calling a UIViewController initializer. View controller: <UITableViewController: 0x7fe1a7407580>'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff203fbbb4 __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007fff2019ebe7 objc_exception_throw + 48
    2   Foundation                          0x00007fff20750da9 -[NSMutableDictionary(NSMutableDictionary) classForCoder] + 0
    3   UIKitCore                           0x00007fff2483b6c6 UIViewControllerMissingInitialTraitCollection + 188
    4   UIKitCore                           0x00007fff2483fbef -[UIViewController traitCollection] + 155
    5   UIKitCore                           0x00007fff2482e93a -[UITableViewController dealloc] + 196
    6   CustomViewControllerInit            0x000000010d33ea3d $s24CustomViewControllerInit0a5TablebC0CACycfc + 157
    7   CustomViewControllerInit            0x000000010d33ea6f $s24CustomViewControllerInit0a5TablebC0CACycfcTo + 15
    8   CustomViewControllerInit            0x000000010d33e99b $s24CustomViewControllerInit0a5TablebC0CACycfC + 27
    9   CustomViewControllerInit            0x000000010d33d50d $s24CustomViewControllerInit0bC0C11viewDidLoadyyF + 109
    10  CustomViewControllerInit            0x000000010d33d55c $s24CustomViewControllerInit0bC0C11viewDidLoadyyFTo + 28
    11  UIKitCore                           0x00007fff2483d2f0 -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 88
    12  UIKitCore                           0x00007fff24841cf4 -[UIViewController loadViewIfRequired] + 1187
    13  UIKitCore                           0x00007fff248420fc -[UIViewController view] + 27
    14  UIKitCore                           0x00007fff2503eb8b -[UIWindow addRootViewControllerViewIfPossible] + 313
    15  UIKitCore                           0x00007fff2503e279 -[UIWindow _updateLayerOrderingAndSetLayerHidden:actionBlock:] + 222
    16  UIKitCore                           0x00007fff2503f26a -[UIWindow _setHidden:forced:] + 409
    17  UIKit                               0x00007fff5963dc30 -[UIWindowAccessibility _orderFrontWithoutMakingKey] + 126
    18  UIKitCore                           0x00007fff25051e5a -[UIWindow _mainQueue_makeKeyAndVisible] + 47
    19  UIKitCore                           0x00007fff252bd80c -[UIWindowScene _makeKeyAndVisibleIfNeeded] + 202
    20  UIKitCore                           0x00007fff24386def +[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1605
    21  UIKitCore                           0x00007fff24ffd75c -[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1253
    22  UIKitCore                           0x00007fff24ffdc0b -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 289
    23  UIKitCore                           0x00007fff24a7e62e -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 358
    24  FrontBoardServices                  0x00007fff26483d98 -[FBSScene _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 419
    25  FrontBoardServices                  0x00007fff264b04a3 __94-[FBSWorkspaceScenesClient createWithSceneID:groupID:parameters:transitionContext:completion:]_block_invoke.180 + 102
    26  FrontBoardServices                  0x00007fff2649221a -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 209
    27  FrontBoardServices                  0x00007fff264b0082 __94-[FBSWorkspaceScenesClient createWithSceneID:groupID:parameters:transitionContext:completion:]_block_invoke + 352
    28  libdispatch.dylib                   0x000000010d4e8c0c _dispatch_client_callout + 8
    29  libdispatch.dylib                   0x000000010d4ebb7f _dispatch_block_invoke_direct + 295
    30  FrontBoardServices                  0x00007fff264d6dc8 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    31  FrontBoardServices                  0x00007fff264d6cbe -[FBSSerialQueue _targetQueue_performNextIfPossible] + 174
    32  FrontBoardServices                  0x00007fff264d6df0 -[FBSSerialQueue _performNextFromRunLoopSource] + 19
    33  CoreFoundation                      0x00007fff20369e25 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    34  CoreFoundation                      0x00007fff20369d1d __CFRunLoopDoSource0 + 180
    35  CoreFoundation                      0x00007fff20369254 __CFRunLoopDoSources0 + 340
    36  CoreFoundation                      0x00007fff20363951 __CFRunLoopRun + 875
    37  CoreFoundation                      0x00007fff20363103 CFRunLoopRunSpecific + 567
    38  GraphicsServices                    0x00007fff2c851cd3 GSEventRunModal + 139
    39  UIKitCore                           0x00007fff24ffbe63 -[UIApplication _run] + 928
    40  UIKitCore                           0x00007fff25000a53 UIApplicationMain + 101
    41  libswiftUIKit.dylib                 0x00007fff5933d052 $s5UIKit17UIApplicationMainys5Int32VAD_SpySpys4Int8VGGSgSSSgAJtF + 98
    42  CustomViewControllerInit            0x000000010d33e088 $sSo21UIApplicationDelegateP5UIKitE4mainyyFZ + 104
    43  CustomViewControllerInit            0x000000010d33e017 $s24CustomViewControllerInit11AppDelegateC5$mainyyFZ + 39
    44  CustomViewControllerInit            0x000000010d33e108 main + 24
    45  dyld                                0x000000010d365e1e start_sim + 10
    46  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIViewController is missing its initial trait collection populated during initialization. This is a serious bug, likely caused by accessing properties or methods on the view controller before calling a UIViewController initializer. View controller: <UITableViewController: 0x7fe1a7407580>'
terminating with uncaught exception of type NSException
CoreSimulator 776.3 - Device: iPhone 8 (1CCAF02C-6FB7-4DDB-A6F7-3CE9009A2EB2) - Runtime: iOS 15.0 (19A339) - DeviceType: iPhone 8
(lldb) 

Although this could be a bug in UIKit, I found three solutions to fix this problem.

  1. Don't subclass UITableViewController, see https://stackoverflow.com/a/30719434/1702958
  2. Don't use a convenience initializer, see https://stackoverflow.com/a/69121381/1702958
  3. Prefix the designated initializer with @objc so UIKit finds your designated init method
@objc init(data: String) {
    super.init(style: .plain)
    self.data = data
}
Convenience init for UITableViewController crashes on iOS 15
 
 
Q