4

App was working fine until Xcode 13 beta 5 builds.

Suddenly get this error in this init line of code in our view controller:

init(dataProvider: DataProvider) {
    self.dataProvider = dataProvider
    super.init(style: .plain)
    dataProvider.delegate = self
}

This code was working fine in Xcode 12 and with no changes it now breaks with:

'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: 0x7f7fbd291bb0>'

I googled around and only found 1 obscure thread on the issue but it wasnt much help.

Sometimes putting a breakpoint and stepping in allows it to work without crashing so there seems to be some sort of layout race condition under the hood but I am struggling to debug. I tried putting a sleep in but that doesn't work.

Any and all advice on how to debug this is appreciated. Right now I am unable to get additional info in order to debug. If you know what might be causing this or where to look I would appreciate your input.

Here is the error stack trace:

*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff203fc8a8 __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007fff2019ebe7 objc_exception_throw + 48
    2   Foundation                          0x00007fff207501c9 -[NSMutableDictionary(NSMutableDictionary) classForCoder] + 0
    3   UIKitCore                           0x00007fff248310ed UIViewControllerMissingInitialTraitCollection + 188
    4   UIKitCore                           0x00007fff24835616 -[UIViewController traitCollection] + 155
    5   UIKitCore                           0x00007fff24824392 -[UITableViewController dealloc] + 196
    6   App Dev                             0x000000010e4f5680 $s05AppLaB027MyViewController
    C14paymentMethods13selectedIndex5offer8delegateACSaySo16AppPaymentMethodCG_SiAA5Offer_pAA08CheckoutcD8Delegate_pSgtcfc + 368
    7   App Dev                             0x000000010e4f5726 $s05AppLaB027MyViewController
    C14paymentMethods13selectedIndex5offer8delegateACSaySo16AppPaymentMethodCG_SiAA5Offer_pAA08CheckoutcD8Delegate_pSgtcfcTo + 102
    8   App Dev                             0x000000010e289291 -[CartViewController showPaymentMethodPickerWithPaymentMethods:] + 385
    9   App Dev                             0x000000010e289055 __59-[CartViewController showPaymentMethodPickerOrEntryForm]_block_invoke + 245
    10  App Dev                             0x000000010e7075ae $sSo16AppServiceResultVSo7NSArrayCSgSo7NSErrorCSgIeyByyy_ABSaySo16AppPaymentMethodCGSgs5Error_pSgIegygg_TR + 222
    11  App Dev                             0x000000010e707246 $sSo17AppAccountServiceC05AppLaD0E19fetchPaymentMethods11forListType7refresh09preferredF6Method10completionSo17AppRequestReceipt_pSgAC0fmiJ0O_S2bySo16AppServiceResultV_SaySo010AppPaymentM0CGSgs5Error_pSgtcSgtFyAN_ArTtcfU_ + 630
    12  App Dev                             0x000000010e707333 $sSo17AppAccountServiceC05AppLaD0E19fetchPaymentMethods11forListType7refresh09preferredF6Method10completionSo17AppRequestReceipt_pSgAC0fmiJ0O_S2bySo16AppServiceResultV_SaySo010AppPaymentM0CGSgs5Error_pSgtcSgtFyAN_ArTtcfU_TA + 35
    13  App Dev                             0x000000010e580474 $sSo16AppServiceResultVSaySo16AppPaymentMethodCGSgs5Error_pSgIegygg_ABSo7NSArrayCSgSo7NSErrorCSgIeyByyy_TR + 212
    14  App Dev                             0x000000010e39bcad __79-[AppAccountV3DAO fetchPaymentMethodsRefresh:preferredPaymentMethod:withBlock:]_block_invoke + 45
    15  libdispatch.dylib                   0x0000000110c18a18 _dispatch_call_block_and_release + 12
    16  libdispatch.dylib                   0x0000000110c19bfc _dispatch_client_callout + 8
    17  libdispatch.dylib                   0x0000000110c28366 _dispatch_main_queue_callback_4CF + 1195
    18  CoreFoundation                      0x00007fff2036a555 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    19  CoreFoundation                      0x00007fff20364db2 __CFRunLoopRun + 2772
    20  CoreFoundation                      0x00007fff20363dfb CFRunLoopRunSpecific + 567
    21  GraphicsServices                    0x00007fff2cbb5cd3 GSEventRunModal + 139
    22  UIKitCore                           0x00007fff24fee193 -[UIApplication _run] + 928
    23  UIKitCore                           0x00007fff24ff2bfb UIApplicationMain + 101
    24  App Dev                             0x000000010e267120 main + 96
    25  dyld                                0x0000000110654e1e start_sim + 10
    26  ???                                 0x0000000000000001 0x0 + 1
    27  ???                                 0x0000000000000001 0x0 + 1
)
pkamb
  • 33,281
  • 23
  • 160
  • 191
Aggressor
  • 13,323
  • 24
  • 103
  • 182

2 Answers2

2

After commenting out a lot of code, I started getting a new error:

fatal error: Use of unimplemented initializer ‘init(style:)’ for class ‘PaymentPickerViewController’

Looking into the behaviour some more, it seems we have an objc initialized extension. I believe in this Xcode 13, the objc code gets mangled and doesn't "see" the root call to the initializer.

Our original init code path was this:

@objc extension PaymentPickerViewController {
    convenience init() {
        dataProvider = DataProvider()
        self.init(dataProvider: dataProvider)
    }
}

init(dataProvider: DataProvider) {
   self.dataProvider = dataProvider
   super.init(style: .plain)        
}

The fix was explicitly overriding the swift init we wanted to use from the super:

override init(style: UITableView.Style) {
    super.init(style: .plain)
     self.dataProvider = DataProvider()
}

This forces the objc to see the correct init and then it works.

I fear the original error that I posted is very misleading so hopefully this helps anyone who runs into this issue.

Aggressor
  • 13,323
  • 24
  • 103
  • 182
  • I have to respectfully disagree. The original error propogates from the objective c being unable to see the initalizer and this only started happening in xcode 13. If you check google theres almost no information on this error. The error itself is misleading and the true source seems to be with objc code being unable to see swift initializers. If there is more clarity you can suggest I am open, but I do believe this answer is the fix. – Aggressor Sep 09 '21 at 16:08
  • I'm having a little trouble evaluating that argument because your code doesn't compile. Is that your real code? For example you say `self.init(dp:)` but you have no `init(dp:)`; you have a different initializer, `init(dataProvider:)`. I don't see how all this hangs together; seems like some pieces of the puzzle are missing. – matt Sep 09 '21 at 17:20
  • I updated the init. This is a mock representation of the code. The root of the issue is: the init method, which was an objc extension in a swift class, was not being seen at runtime by the objc code. By explicitly overriding the the tableview init we needed to use, it fixes the issue. – Aggressor Sep 09 '21 at 17:39
  • This answer talks more about why the compiler doesn't see the init: https://stackoverflow.com/a/28187653/3324388. Why this started happening suddenly in Xcode 13 and why it propagates as the error I originally posted, only Apple can tell you that. – Aggressor Sep 09 '21 at 17:42
  • 1
    Before I dive into this further, is my answer here relevant? https://stackoverflow.com/a/30719434/341994 – matt Sep 09 '21 at 18:30
  • 1
    I don't believe so, no. – Aggressor Sep 09 '21 at 18:58
0

Short description: For objective-C based implementation, could convert the object methods to class methods.

The following error was being reported on xcode13 GM Seed. Error Log:

*** 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: <MyViewController: 0x7fa5ae2356c0>'

For the following init method:

- (instancetype)initWithModel:(MyModel *)myModel {
    
    MyViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"MyViewController"];
    vc.model = myModel;
    return vc;
}

init is invoked in the following manner from another View Controller.

// Before - invoking object method -- Results in the error
MyViewController *viewController = [[MyViewController alloc] initWithModel:testModel];

// After - invoking Class method
MyViewController *viewController = [MyViewController initWithModel:testModel];

In order to resolve it, I converted the init method to a class method.

+ (instancetype)initWithModel:(MyModel *)myModel {
    
    MyViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"MyViewController"];
    vc.model = myModel;
    return vc;
}
lal
  • 7,410
  • 7
  • 34
  • 45