Compiling XIBs with CMake without Xcode

I’ve been enjoying using the JetBrains IDE CLion to do some refactoring and improvements to the Auctions code base. However, when I tried to build the Mac app bundle with it, the app failed to launch:

2022-07-30 19:54:15.117 Auctions[80371:16543044] Unable to load nib file: Auctions, exiting

The XIB files were definitely part of the CMake project. I later learned that CMake does not automatically add XIB compilation targets to a project. It relies on the Xcode generator to do that.

I found a long-archived documentation page from CMake on the Kitware GitLab that described a method to build NIB files from XIBs, and have modified it to make it simpler for Auctions.

You can see the change in the commit diff, but I’ll include the snippet here for posterity.

First, you define an array with the XIB file names with no suffix. For instance, I’ve done set(COCOA_UI_XIBS AXAccountsWindow AXSignInWindow Auctions) for the three XIB files presently in the codebase.

Then we have the loop to build them:

find_program(IBTOOL ibtool REQUIRED)
foreach(XIBFILE ${COCOA_UI_XIBS})
add_custom_command(TARGET Auctions POST_BUILD
COMMAND ${IBTOOL} --compile ${CMAKE_CURRENT_BINARY_DIR}/Auctions.app/Contents/Resources/${XIBFILE}.nib ${CMAKE_CURRENT_SOURCE_DIR}/${XIBFILE}.xib
COMMENT "Compiling NIB file ${XIBFILE}.nib")
endforeach()

Now it starts correctly and works properly when built from within CLion. This was surprisingly difficult to debug and fix, so I hope this post can help others avoid the hours of dead ends that I endured.

Until next time, Happy Hacking!

Designing a Sheet in Interface Builder

Quick tip time! This is an anecdote from a libre project I’m working on, specifically Auctions.

One of the things I am doing right now is implementing the Cocoa/Mac UI. I’m writing a flow using sheets for signing in to accounts. I had a lot of issues making the sheet accept input; it just wouldn’t let any of its fields become the first responder.

Poking around DuckDuckGo, I found a Stack Overflow question that seemed pretty interesting, and the answer was to override NSPanel‘s canBecomeKeyWindow method to return YES. I did some searching around in Apple’s Developer Documentation to see how the system determines when a window can become a key window, and I found this nugget:

A window that uses NSWindowStyleMaskBorderless can’t become key or main, unless the value of canBecomeKeyWindow or canBecomeMainWindow is YES. Note that you can set a window’s or panel’s style mask to NSWindowStyleMaskBorderless in Interface Builder by deselecting Title Bar in the Appearance section of the Attributes inspector.

Apple Developer Documentation

I had turned off Title Bar in Interface Builder as I thought it should be disabled since the window would be shown as a sheet. I re-enabled Title Bar, and voila! The sheet worked perfectly, and did not have a title bar when displayed as a sheet.