Tuesday, April 14, 2015

Fixing Xcode 6.3 (iOS SDK) Error: Could not load NIB in bundle

After the recent update of Xcode to version 6.3 iOS developers reported to encounter exceptions of kind


Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: Could not load NIB in bundle: ...

followed by the path to the running iPhone or iPad application, and the name of the NIB file the it was trying to load. These exceptions appear to fire at runtime only, not at compile time. So make sure you test your iOS applications completely after building them with Xcode 6.3!

The reason for this issue seems to be a bug of Xcode 6.3 when compiling XIB files using size classes. Size classes is a concept introduced with iOS 8 which enables you to define view layouts for different UI orientations and different iPhone and iPad devices within a single Interface Builder document. A very good introduction to size classes is given at adaptioncurve.net.

So let's have a look at a little example to elaborate the problem! 

We have an Xcode project with the deployment target set to iOS 7 and Universal device support:











We have two XIB files: TestSizeClasses.xib is targeted to iPhone and iPad. TestSizeClassesIphone~iphone.xib is named according to Apple's conventions to be targeted to iPhone only:










Both XIB files have size classes enabled:















After compiling these files with XCode 6.2, and running them in Simulator let's have a look at the NIB files created at compile time. You will find these NIBs in your user directory under a path of this structure:
~/Library/Developer/CoreSimulator/Devices/<cryptic_device_number>/data/Containers/Bundle/Application/<cryptic_app_number>/<app_name>.app

In order to identify the correct cryptic number directory, just sort directories by modification date and choose the last modified one.

Xcode 6.2 created these three NIB files:





TestSizeClasses.xib was split into two device specific NIB files, but TestSizeClassesIphone~iphone.xib was compiled to a single, iPhone specific NIB file because the '~' - part of the file name signals to the compiler that we care about device specific layouts by ourselfs. So everything is fine!

Now lets compile the same files with Xcode 6.3 and have a look at the results:





As you can see, Xcode appends another '~iphone' part to the name of TestSizeClassesIphone~iphone.nib, although we wanted it to leave our file name alone! So this is the bug in Xcode 6.3 XIB processing which will lead to runtime 'could not load nib' exceptions when your app tries to load the badly named nib file. 

The bug will haunt you when you have size classes enabled for a XIB file, and the deployment target of your Xcode project is set to something earlier than iOS 8. Because size classes are only supported in iOS 8.0 and later, Xcode will compile the old device specific nib files automatically for older iOS versions. It doesn't matter whether your app is universal or targeted to iPad or iPhone only. As soon as you name a XIB device specific using the '~', your app will crash.

Now that we know the reasons for this exception, we have four options to handle it:



Option 1: Target to iOS 8.0 or later 

The simplest option would be to target your app to iOS 8 or later, thus the device specific NIB files would just not be created. But if you want to support older iOS versions, this is not an option for you.

Option 2: Use storyboards instead of  XIB files

If you can afford to make the effort, migrate all your XIBs to storyboards. These will be compiled correctly by Xcode 6.3.

Option 3: Disable Size Classes

When you disable size classes for a XIB file, Xcode will force you to target this file to a specific device and will remove all data it thinks will not be of use to represent this device:












So choose this option only when you absolutely trust in that Xcode will not remove anything you need from your XIB files. As a rule of thump: If all layout constraints in a XIB apply to all size classes, you are safe. If different constraints apply to different size classes, think twice!

Option 4: Handle device specific XIBs in code

This is the less destructive of all options and it is not as expensive to implement as it might seem! All you have to do is to name your XIB device specific by avoiding Apple's naming conventions (the '~'), and add very few lines of code to load the correct NIBs.

So we rename our TestSizeClassesIphone~iphone.xib to TestSizeClasses-Iphone.xib. We create another TestSizeClasses-Ipad.xib to cover all devices. And we create a  single TestSizeClassesViewController, which we set the file's owner of both XIB files:










After compiling the code, we'll find the following NIBs:









As you can see, Xcode created both, an ~iphone version and an ~ipad version of all of our NIB files. Now all we have to do is to tweak our Objective-C code to load the correct NIB version per device.

So open TestSizeClassesViewController and implement its init method to load device specific nib files:

- (id)init
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    {
        return [self initWithNibName:@"TestSizeClasses-iPad" bundle:nil];
    }
    else
    {
        return [self initWithNibName:@"TestSizeClasses-iPhone" bundle:nil];
    }

}

Do the same in all places of your code where the view controller is instantiated directly using initWithNibName:

...
TestSizeClassViewController* sizeClassController;    
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
    sizeClassController = [self initWithNibName:
        @"TestSizeClasses-iPad" bundle:nil];
}
else
{
    sizeClassController = [self initWithNibName:
        @"TestSizeClasses-iPhone" bundle:nil];
}

...

This way, the app will load the TestSizeClasses-Iphone~iphone.nib and TestSizeClasses-Ipad~ipad.nib only. It will ignore the TestSizeClasses-Iphone~ipad.nib and TestSizeClasses-Ipad~iphone.nib.


That's all there is about this Xcode issue. Now it's up to you to decide which of the four options is the best for your needs.




15 comments:

  1. Hi! Thanks for this post, but I have little bit different problem. My application crashes on iOS 8.3 on the very same error, but I use Storyboards. There is no XIB in my project.

    Do you have any idea? Why?

    ReplyDelete
  2. Hi Tomáš, in order to help you I need a little more details like exact exception stacktrace, names and content of storyboard files, and content of deployment directories in iOS simulator.

    ReplyDelete
  3. Your Article provides useful information for more details visit flashify apk download zarchiver android package disabler pro apk

    ReplyDelete
  4. Some of us may not know the importance of Artificial Intelligence in E-commerce store. This blog helps me to know the benefits of AI in the business. Great Work!
    Hire wordpress developer India
    Hire Dedicated Programmers
    Hire Php Programmer
    Opencart Developers India
    Hire Magento developer India

    ReplyDelete
  5. In today's world, having online presence plays a significant role for your brand recognization. Hire dedicated developer India can help you to develop best business presence by eliminating risk, training, infrastructure cost, and many more things. There are many apps and web development companies that outsource developers to assist with the project. They are geared with extensive knowledge of the latest tools and technologies. You can usually hire a programmer to complete a project for one fixed price. However, this doesn’t allow you to easily make changes as your business changes and grows. Change requests come at an additional cost so there is not much flexibility. With fixed-price development, the programmer is not dedicated to your business so you are oftentimes less likely to get everything you want to be done when you want it. This approach is less flexible and more time-consuming. It is far better to hire dedicated developers through a development company because you are protected from fraud most likely going to have more options for experience and pricing. You will also have a team of people at your disposal for greater productivity vs 1 person. This can make things much easier when trying to get your project completed.

    ReplyDelete
  6. Thanks for sharing insight full thoughts I really enjoyed this post.
    Annotation Tool

    ReplyDelete
  7. If you want to upgrade Magento version you need following steps.
    Backup your Magento store:
    Creating backups for your Magento 2 site is so urgent and necessary that you can protect all data from the disappearance through Backup Management if there is any change or break on the site. Follow this guide to backup your Magento 2 store.

    You need on Your maintenance mode:
    You should put your store in maintenance mode while upgrading. To enable maintenance mode:
    php bin/magento maintenance:enable
    It will create a new file var/.maintenance.flag. If you cannot disable maintenance mode, you can remove this file [Remember!]

    Upgrade to your Magento version:
    In this case, I will upgrade to Magento version 2. See latest releases at to read more just click here. https://www.magemonkeys.com/how-to-upgrade-magento-version-from-2-3-2-to-2-3-5/

    ReplyDelete
  8. Really I enjoy your site with effective and useful information. It is included very nice post with a lot of our resources.thanks for share. i enjoy this post.
    Microsoft Dynamics CRM Support
    Microsoft Dynamics CRM Training
    Microsoft Dynamics CRM Migration
    Microsoft Dynamics Development & Customization
    Microsoft Dynamics CRM Integration

    ReplyDelete
  9. Thank you for providing this article Free iOS App Building Software. Through it, I gained some useful knowledge. Thank you for writing such a great post.

    ReplyDelete