Search code examples
xcodeexceptionbetaxcode14

Xcode 14 Beta 5 throws an exception


Xcode 14 Beta 5 shows this exception:

[<_UINavigationBarContentViewLayout valueForUndefinedKey:]: this class is not key value coding-compliant for the key inlineTitleView.

I am getting this new exception in all my obj-c projects while using Xcode 14 Beta 5.

A few notes:

  • Exception appears on any simulator device (running iOS 16)
  • Exception appears right on the start inside int main(int argc, char * argv[])
  • No exception on real device running iOS 15
  • Exception can be ignored (no crash).

I wonder if anyone else encountered this.


Solution

  • This is a bug in Xcode 14. Other users have reported it here: https://developer.apple.com/forums/thread/712240

    Initially, the issue was reported in Xcode 14 betas, but the bug was never fixed, and now, here we are. I reproduce the issue in the official release of Xcode 14.0.1.

    Here are workarounds that I've tested that will work:

    1. Use a physical device: The issue doesn't occur when testing on a physical iOS device. I tested on an iPhone 13 Pro running iOS 16.0 with Xcode 14.0.1. The error doesn't occur then/there.

    2. Ignore Objective-C exceptions: When the issue occurs, an Objective-C exception is thrown. Those are usually critical errors, but this particular exception is bogus. By default, Xcode always pauses the debugger when your app throws an Objective-C exception, but you can tell Xcode to stop doing that. In Xcode, go to the View --> Navigators --> Breakpoints menu. There, you'll see any/all breakpoints you have set. But one of the breakpoints appears there by default: "All Objective-C exceptions"; it has a dark blue arrow next to it, indicating that, yes, Xcode will indeed pause the debugger on all Objective-C exceptions.

      "All Objective-C Exceptions" breakpoint

      If you click on that blue arrow, it will turn light blue, disabling the breakpoint. From then on, Xcode will ignore this exception, but, unfortunately, it will ignore all exceptions, even real exceptions that you actually care about. This may be enough to get you unstuck, but we'd normally want Xcode to pause on thrown exceptions (because they're normally very serious).

    3. Add a condition to the "All Objective-C Exceptions" breakpoint: Since it's not great to just completely disable this breakpoint, we can do something cleverer. Enable the breakpoint (make sure its arrow is dark blue), then right-click on it and "Edit Breakpoint..." There, you can paste in this condition: !(BOOL)[(id)[$arg1 reason] containsString:@"_UINavigationBarContentViewLayout"] That will cause the breakpoint to pause execution on all breakpoints except breakpoints that contain the string _UINavigationBarContentViewLayout. Pretty good, I think! But every developer on your team will have to do this on every machine they use for testing. (Credit goes to Grigory Entin's comment)

    4. Monkey-patch UINavigationBarContentViewLayout: Objective-C is a dynamic language, allowing you to add methods to a class at runtime. This is called monkey patching. It's normally unwise to do that, and it's normally especially unwise to do it to Apple's code. Apple normally won't allow you to submit apps to the store that monkey patch Apple's classes. But, as long as you're just doing it on your machine, and just to workaround a bug in the Xcode simulator, there's no harm in it, right?

      The code sample here is based on a post from the Apple Developer Forum by moshiwu.

      #import <objc/runtime.h>
      
      @interface Xcode14Fixer : NSObject
      @end
      
      @implementation Xcode14Fixer
      
      + (void)load
      {
          Class cls = NSClassFromString(@"_UINavigationBarContentViewLayout");
          SEL selector = @selector(valueForUndefinedKey:);
          Method impMethod = class_getInstanceMethod([self class], selector);
      
          if (impMethod) {
              class_addMethod(cls, selector, method_getImplementation(impMethod), method_getTypeEncoding(impMethod));
          }
      }
      
      - (id)valueForUndefinedKey:(NSString *)key
      {
          return nil;
      }
      
      @end
      

      Put that code at the top of your AppDelegate.m file, and then, at the top of your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { method, add this line: [Xcode14Fixer load];

      moshiwu attempted to use #if DEBUG to ensure that the code would only be used in debug mode, but that didn't work for me. Instead, just promise yourself that you'll remember to remove this debug code before you ship an official build to Apple, or Apple will probably reject your build.