Splitview controller is expected to have a master view controller

Whilst playing with the Master Detail Application template provided with Xcode 4.3.2 I came across an odd error message for the split view controller class. The problem happens when you implement the UISplitViewControllerDelegate method that specifies whether the master view controller should be hidden in a given orientation. The splitViewController:shouldHideViewController:inOrientation method was introduced in iOS 5.0 and is useful when you want to change the default behaviour which hides the master view in portrait mode. See this earlier post on iOS 5 split view controller changes for the full details.

To reproduce the problem create a new project using the Master Detail Application template and implement the following method in the DetailViewController:

- (BOOL)splitViewController:(UISplitViewController *)svc
   shouldHideViewController:(UIViewController *)vc
              inOrientation:(UIInterfaceOrientation)orientation {
  return YES;
}

Now when you run the project in the iPad Simulator you get the following error message logged to the console:

Splitview controller <UISplitViewController: 0xbec57e0> is expected
to have a master view controller before its used!

Clearly an attempt to use a split view controller without giving it the two view controllers that it expects would be a serious error and probably deserves an immediate abort. However in this case we are using unmodified template code from Apple which does configure the split view controller with both a master and detail controller. The App also appears to run fine so the warning message does at first seem puzzling.

I noticed a while back that Apple changed the style of many of the example iOS projects to construct the initial root view controller interface in code rather than load it via a NIB file. The Master Detail application template also follows that trend. The following code snippet is from the point in the application delegate where the UISplitViewController is allocated and configured with its delegate and the two view controllers for the master and detail views:

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...
  ...
  self.splitViewController = [[UISplitViewController alloc] init];
  self.splitViewController.delegate = detailViewController;
  self.splitViewController.viewControllers = [NSArray
                           arrayWithObjects:masterNavigationController,
                           detailNavigationController, nil];
  ...
  ...
}

This looks fine but notice that the split view controller delegate is set before the viewControllers. This seems to be the source of the problem and if you reverse the two lines as shown below the warning message disappears:

self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.viewControllers = [NSArray
                         arrayWithObjects:masterNavigationController,
                         detailNavigationController, nil];
self.splitViewController.delegate = detailViewController;

So it seems that the UISplitViewController performs some work in the setter for the delegate property and that this relies on the viewControllers already being set. I cannot find anything in the documentation that mentions this and it appears that whoever wrote the Xcode template was also unaware of this requirement.

In this case it does not seem to be a serious issue and the fix is trivial but if you come across this error message you may want to check if you have your viewControllers configured before you set the split view controller delegate. In the meantime I am thinking this is worth a bug report to Apple to see if they can provide some clarification in the UISplitViewController class documentation.