Xcode: Who’s to Blame?

A recent tweet from Dave DeLong reminded me of something that’s been on my mind for a long while.

As software developers, we all screw up. We all check in software with accidental bugs. We’re all constantly coming up to speed with new frameworks, tools, APIs, language features, concepts, and best practices. We’re all trying to accomplish a lot in a limited amount of time.

I think the hashtags Dave used, #BeNice and #DontBeAJerk, convey the sentiment that we should have empathy for each other as developers and as human beings.

Of course, all software has room for improvement and I also think it’s perfectly valid to vent frustrations about the tools you use professionally.1

But I don’t think disparaging the people who work on the software or attributing bad motives to them is helpful at all.

That said, I think it’s fair that the same principle should extend to Xcode itself, especially something that has bothered me for a long time: Blame.

Blame is “responsibility for a fault or wrong” — a word with a very negative connotation.

Screen capture of 'Show Blame for Line' contextual menu item

In Xcode, every line of code is a mea culpa.

Blame is pointing fingers without solving the issue at hand.

In Xcode, the Blame view and the Show Blame For Line menu item imply that the code you or anyone else writes is a reason to be embarrassed and ashamed.

According to the Xcode user interface, every line of code written for Apple platforms is something that should be apologized for—every app in the App Store built from culpable actions and misdeeds.

Blame doesn’t belong in the user interface of Xcode.

Words Matter

A parody of the Swift logo, with a flounder instead of a swift

There’s a reason it’s not called Flounder.

You could certainly make the case that it’s just a word and no big deal. But I believe words matter.

If you clicked on this article based on the title, thinking you would be reading an accusatory, negative piece about Xcode, then you have just experienced the power of the word blame.

And folks at Apple know that words and their connotations matter. There’s a reason it’s called ‘Swift’ and not ‘Sloth’ or ‘Flounder’. There’s a reason we’ll probably never see ‘macOS Death Valley’ even though, like Yosemite, it’s a National Park in California.

That’s How Git Does It

Screen capture of hypothetical Xcode showing an Author menu item instead of Blame

Author! Author!
in an Xcode without Blame.

You could also make the case that blame is the command name in git. But the git command diff corresponds the Comparison view in Xcode. So using exact git terminology in Xcode is not sacrosanct. Even if it was, git has the equivalent and less pejorative annotate.2

Of course, Apple has a long history of forging its own path, regardless of what others in the industry are doing. One could imagine taking the same approach as with the Comparison view and using a name that has nothing to do with an existing git command. The icon in the Version editor pop-up menu already shows the outline of a person. That view could possibly be called the Author view, with a corresponding Show Author For Line menu item.

Just A Joke

You could also say that blame is just a wry bit of humor and if everyone knows it, then it lessens the negative connotation.

But not everyone is necessarily in on the joke. An example is people who are just learning to write code and develop apps. Learners, whether children or adults, can have a fear of failure when trying something new, compounded by a fear of being ridiculed for failing when trying. People new to programming don’t know all of our witty little inside jokes, they just know that Xcode is assigning blame to their efforts.

Apple has been touting the Everyone Can Code program for a few years now, including courses on building apps in Xcode. It think it is great that Apple is making such efforts to teach programming skills to a wide audience. But blame doesn’t belong in a learning environment.3

Sign Your Work

During the development of the original Macintosh, Steve Jobs decided to include each team member’s signature on the inside of the case. The signatures were engraved in the tool that molded the case of every early Mac produced.

This wasn’t so customers would know who to blame. This was because artists sign their work.

Similarly, as software developers, our names are embedded in every commit. They are a record of our work and effort, a record of every success and failure, a record of every hard-won lesson learned.

Whether you are a complete newcomer or an experienced coder, you should be able to take pride in your work as a developer. Your tools should do their best to help you. Your tools should acknowledge and, if possible, even celebrate your authorship, not disparage it.

Please take the blame out of Xcode.

It’s one small change in a .strings file, but may be one giant leap for our coding culture. •

I filed a Radar about this issue in June 2011 while still working at Apple—it was closed as Not To Be Fixed. Today I filed another bug report which you can see on OpenRadar. Feel free to file duplicates.


But don’t expect venting to change anything. The best approach is still to file a bug. That’s no guarantee your issue will be addressed, but you’ve provided the constructive feedback. Of course, the opacity of the Apple bug reporting system has its own frustrations and is its own can of worms.

Which is admittedly a less catchy name.

In Xcode 9, ‘Show Blame For Line’ is no longer in the code editor contextual menu in Xcode Playgrounds, but it’s still in the Editor menu. But students also want to build apps, and outside of Xcode Playgrounds, blame is still in full force.

New Xcode Build System and BuildSettingExtractor

Last week at WWDC 2017, Apple announced a new build system for Xcode, with an opt-in preview in Xcode 9. The new system is written in Swift and promises to be a significant advance in a number of areas including performance and dependency management. The new system is built on top of the open source llbuild project and lays the foundation for integrating the Xcode build system with the Swift Package Manager.

You opt in to the new build system per project or workspace. To do so in the Xcode 9 beta, open a project file and then go to File > Project Settings… and in the sheet that appears, choose New Build System (Preview) from the Build System popup menu. Note that the menu item will be File > Workspace Settings… if you are working with a workspace.

Project settings sheet with popup menu to select build system

Opt-in to the new build system for a project in File > Project Settings…
(For a workspace use File > Workspace Settings…)

The big news of a new build system made me curious to see if there were any changes I would need to make to BuildSettingExtractor.

Icon for the app BuildSettingExtractor

BuildSettingExtractor helps you move to xcconfig files.

If you are not familiar with it, BuildSettingExtractor is an open source utility that helps you move to using xcconfig files. It reads the build settings embedded in an Xcode project file and extracts the settings into xcconfig files. It can also extract the Xcode help for each build setting to produce automatically documented configuration files. (Read my prior post Generating Xcode Build Configuration Files with BuildSettingExtractor for more about the benefits of using xcconfig files.)

My investigation led me to look more closely at what was changing and what was staying the same when it came to the new build system and build settings in Xcode 9.

When it comes to build settings, there are two big operations: Defining build settings and using build settings.

As developers, we spend our time on the first part, defining build settings1. In a complex project or workspace, this can be an involved process. Defining build settings includes all of the following:

  • Build settings defined for each target and for the project itself
  • Variable substitution to define derived build settings using ${} or $()
  • Conditional build settings based on build configuration, architecture, and SDK
  • Optionally using xcconfig files to define build settings
  • Understanding the well-defined hierarchy of how build settings are inherited and used

In the end, these intricate and flexible mechanisms for defining build settings resolve into a big dictionary of key-value pairs that is passed to the build system.

The build system is what uses the build settings to help direct how it builds a target. The build system coordinates the various tools required, such as the compiler and linker. It uses a target’s build phases and build settings to generate a build product (e.g. app, extension, framework, library). It understands all of a target’s dependencies and builds them as well. The build system is a complicated beastie with a lot of responsibilities and it is what is being modernized beginning in Xcode 9.

Logo of new Xcode build system

New build system.
Same build settings.

On a number of my projects, I’ve switched from the current build system to the new build system in Xcode 9 to investigate. It appears that everything about defining build settings remains unchanged. Moving between the old and new build systems did not cause any build setting changes or recommended changes. The mechanisms for generating that giant bucket of key-value pairs known as build settings seem to be just the same as before.

This is great news. As developers, we don’t need to learn a new complex system for defining build settings. We get to enjoy the benefits of a new, faster, modern build system with our existing build settings left intact.

As the developer of BuildSettingExtractor, this is also great news—no big changes required. After doing some testing and a tweak here and there, BuildSettingExtractor is now updated to work with the Xcode 9 beta. I invite you to check out BuildSettingExtractor and the new build system in Xcode 9. •


1And debugging them.

Learning More About Xcode Build Settings with BuildSettingExtractor

Last week I posted about BuildSettingExtractor, a utility that makes it easy to pull the build settings out of an Xcode project file and into xcconfig build configuration files. (The post also mentions some xcconfig file benefits with links to ‘how to’ information. Read the earlier post here.)

I also wanted BuildSettingExtractor to be useful for anyone who wants to learn more about the build settings in their projects. To that end, the latest version, available on github, generates build setting descriptions gleaned from the installed version of Xcode.

For example:

// Framework Search Paths
// 
// This is a list of paths to folders containing frameworks to be
// searched by the compiler for both included or imported header
// files when compiling C, Objective-C, C++, or Objective-C++, and by
// the linker for frameworks used by the product. Paths are delimited
// by whitespace, so any paths with spaces in them need to be
// properly quoted. [-F]

FRAMEWORK_SEARCH_PATHS = $(DEVELOPER_FRAMEWORKS_DIR) $(inherited)
	

// Info.plist File
// 
// This is the project-relative path to the plist file that contains
// the Info.plist information used by bundles.

INFOPLIST_FILE = BuildSettingExtractorTests/BuildSettingExtractorTests-Info.plist

Inline Build Setting Info

To learn more about build settings, Apple provides the Build Setting Reference (Apple developer documentation links are notoriously fragile, just search for the document) as well as Quick Help in Xcode when a build setting is selected. (In Xcode choose View > Utilities > Show Quick Help Inspector (Cmd-Opt-2))

In addition to these ways of learning more, BuildSettingExtractor gleans the build setting info from Xcode and puts it inline in the generated xcconfig files. This has a few benefits:

  • Read about each build setting without selecting setting one by one in Xcode
  • Read about only the build settings currently set in your project and targets
  • Information about build settings is available wherever you are looking at the xcconfig file: github, text editors, diff tools, etc.

What if I don’t want this lovely but verbose feature?

If you want BuildSettingExtractor to generate pithy xcconfig files without the build setting info, Choose BuildSettingExtractor > Preferences… (Command-,) for the new preferences sheet and turn it off.

BuildSettingExtractor Preferences Sheet

Happy Building! (and a call for help)

I hope you find BuildSettingExtractor useful, either as a learning tool, or to get rolling with xcconfig files. If you do find it useful, please spread the word about it: https://github.com/dempseyatgithub/BuildSettingExtractor.

As for the call for help, if you would like to help out with a basic app icon for BuildSettingExtractor, the utility can emerge from the primordial world of the generic app icon. Contact me if you are interested. •

Generating Xcode Build Configuration Files with BuildSettingExtractor (xcodeproj → xcconfig)

The most recent NSScreencast covered using build configuration files to specify build settings in Xcode. (Episode #154)

Using build configuration files—or xcconfig files as they are known—has some definite benefits.  However, as seen in the screencast, the initial process of copying the current build settings out of an Xcode project into xcconfig files is tedious and potentially error-prone.

Screenshot of Build Setting Extractor 1.0

Nuthin’ fancy. Drop an xcodeproj file on it—it spits out xcconfig files.

To aid in that initial extraction process, I wrote a utility app called BuildSettingExtractor.  It is available on github at https://github.com/dempseyatgithub/BuildSettingExtractor.

The app is a simple droplet utility: drop an xcodeproj file on it, choose a destination folder, BuildSettingExtractor will extract the build settings from the project and generate xcconfig files.

A set of files is generated for each target in the project and for the project itself.  Each set of files includes one xcconfig file per build configuration and one xcconfig file of shared settings. For example, a typical Xcode project will generate nine xcconfig files: three sets of files for the app target, test target, and project with three files for Debug, Release and Shared in each set.

I hope you find BuildSettingExtractor useful. Even if you are just curious about project build settings, this is an easy way to inspect a project’s build settings without fear of accidentally changing them.

A little more about xcconfig files

By default, Xcode stores all build configuration settings in the project file itself. However, you can tell Xcode to base a build configuration’s settings on a build configuration file instead.

A build configuration file is a text file of key-value pairs. An xcconfig file can also contain comments and include other xcconfig files.

//
// Project-Debug.xcconfig
//

#include "Project-Shared.xcconfig” // Include other xcconfig files

// An xcconfig file is a text file of key-value pairs.
// Use comments to record why you are using certain build values.
// The /*comment*/ and #comment styles are not valid in an xcconfig file.

COPY_PHASE_STRIP = NO
GCC_DYNAMIC_NO_PIC = NO
GCC_OPTIMIZATION_LEVEL = 0
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited)
GCC_SYMBOLS_PRIVATE_EXTERN = NO
MTL_ENABLE_DEBUG_INFO = YES
ONLY_ACTIVE_ARCH = YES

There are some benefits to using xcconfig files:

  • Build settings are not in project file—this removes one source of project file merge conflicts
  • Build settings can be documented using comments
  • An xcconfig file of shared settings can be included in each build configuration

There are also potential drawbacks to using xcconfig files.  One potential pitfall is that the build settings for a configuration are based on the xcconfig file—but settings set in Xcode override the xcconfig file settings. This can leave you with unexpected build behavior because an errant build setting set in Xcode is overriding the xcconfig file setting.

This issue is compounded by the fact that when new Xcode versions update project settings, the settings are added to the project, not to an xcconfig file.  So when using xcconfig files, some vigilance is required to keep an eye out for build settings being added to the project.  These stray build settings should be moved from the project to the appropriate xcconfig file.

What Next?

BuildSettingExtractor generates xcconfig files from a project—it does not set your project up to use them.  Here are few resources that walk through that process:

I was inspired to release BuildSettingExtractor by Episode #154 of NSScreencast. The episode is a step by step demo walking through the process.

By the way, if you aren’t familiar with NSScreencast, I recommend taking a look. Ben Scheirman (@subdigital) presents weekly bite-sized screencasts covering a wide range of development topics. Some screencasts are free and some, like Episode #154, require a subscription. (Ben also plays guitar with James Dempsey and the Breakpoints, but that has yet to be the subject of a screencast.)

The blog post Using xcconfig Files For Your Xcode Project by Jont Olof Lyttkens (@jontolof) explains how to set up a project to use xcconfig files in Xcode 6.

Finally, Building the Build System by Rob Napier (@cocoaphony) is an older post, written for an older version of Xcode, but the overall concepts and explanation of the benefits are still relevant.

In each of these walkthroughs, instead making a bunch of xcconfig files by hand and then copying and pasting from the project into the correct file, you would use BuildSettingExtractor instead. •

Getting an app ready for iPhone 5

I was traveling the week between the iPhone 5 announcement and release. I returned from my trip champing at the bit to update my app WALT to use the entire 4-inch display, instead of being stuck in the letterbox ghetto. Updating turned out to be simple and straightforward.  Here’s how it went.

Out of the letterbox

The first step was getting out of the letterbox.  To do this, I upgraded the project to use the iOS 6 SDK and added a new 640 × 1136 launch image for the new screen size. Like all launch images, it needed to have a particular name: Default-568h@2x.png. There’s a lot of information encoded in that file name—the default launch image name, the screen height, the high-resolution size and the image format.

Enabling a feature by adding a specially-named image is a clever and compact approach, but it is not very obvious or discoverable on its own.  Xcode 4.5 helps by displaying a project warning and offering to create the new launch image for you.  (Although I see the warning now in a test project, I don’t remember seeing it when I updated WALT.)

Xcode 4.5 adds a few other features to support development for Retina 4 displays. The iOS Simulator has a new hardware device setting so you can test your app with the new size, even if you haven’t been able to get your hands on an iPhone 5.

When viewing storyboards, Interface Builder makes it very easy to toggle between Retina 3.5 and Retina 4 sizes using a newly-added resize button.

Resize button in Interface Builder

New resize button toggles storyboards between 3.5 and 4-inch views

If you are not using storyboards, Interface Builder still has you covered.  You can change the screen size for a top-level view in the Simulated Metrics section of the attributes inspector. This setting is also available with storyboards, but I have found the single toggle to be much more convenient to use.

Screenshot of simulated metrics options in Interface Builder

Choose which size to work with in a xib

Mostly done already

Since WALT uses standard tab, navigation and table views, there was nothing additional required to effectively use the additional screen space. Each table view became taller and now displays more content. I could have released the app as-is, but wanted to do a few tweaks.

On a Retina 3.5 screen, the film detail view in my app adjusts row heights in certain cases to improve the layout.  I needed to make different adjustments for Retina 4 screens, so I needed to tell the two screen sizes apart.  The new screen size is not a different user interface idiom—just a taller iPhone. For screen size, the highest level API available is to use UIScreen to get the height of the main screen:

 CGFloat mainScreenHeight = [UIScreen mainScreen].bounds.size.height;

I‘ve already seen a number of online examples that use the screen size to add convenience category methods to UIScreen or UIDevice.  In my case, I checked the screen size inline in the one place where it was needed.

Screenshot of the film detail of 'Partysaurus Rex' in the app WALT.

What Up, Fishes?!
The newly added ‘Partysaurus Rex’ looks nice and comfy, all stretched out on a beautiful iPhone 5 in the new version of WALT.

Although my experience was quick and painless, the amount of work needed to adapt an app for 4-inch screens varies greatly depending on its contents.  The iOS Human Interface Guidelines document suggests different approaches for various situations. It also makes a very important point that applies to all apps.  An app should remain the same app, with the same features and interaction, regardless of which screen size it is being displayed on.  But, when displayed on a 4-inch screen, it should reveal more of its content.

In summary, getting your app to stretch out to the full iPhone 5 screen is as easy as updating to the latest SDK and adding a new launch image.  Getting your app to look great, however, will depend on how customized your views are.

For me, I found that getting the app ready for iPhone 5 took less time than preparing the new set of taller screenshots required for the App Store. •

WALT 1.0.1 is available now in the App Store with iPhone 5 and iOS 6 support.

Shipping a first iOS app

My first iOS app, WALT, went live recently, and I wanted to share some of my thoughts on the development experience.  Overall, there were two big differences from past projects I have worked on.

First, since most of my past Cocoa development has been focused on the Mac, building and shipping an iOS app meant learning a new app framework and some new ways of going about things.  It has been an enjoyable, but odd, combination of exploring things which are a good deal different, yet simultaneously very familiar—like walking into someone else’s kitchen for the first time, but already knowing where they keep most everything.

Second, shipping an app as an independent developer is a very different experience than shipping a product as part of a large team at Apple. Intellectually, I knew this from the start.  Even so, knowing about it doesn’t necessarily prepare you for all of the implications until you’ve lived through them.

One implication, of course, is that I was able to build the app I wanted to create.  I had a few specific goals for a first app:

  1. Create an app that I wanted to use
  2. Focus on frameworks and fundamentals
  3. Build a solid, speedy, polished app
  4. Ship it

Create an app that I wanted to use

Since childhood I’ve been a fan of Disney (and later Pixar) animation as an audience member—suspending disbelief and just enjoying the experience.  Over time, I’ve increasingly come to appreciate the incredible amount of artistry, collaboration and process that goes into each film.

One source of this growing appreciation is The Walt Disney Family Museum in San Francisco, which describes the evolution of animation as part of its narrative.  I’m inspired by things such as the incredible advancements and innovations in animation from the late 1920s to the late 1930s.  Over a ten year period, animated films progressed from being black and white series of sight gags using ‘rubber hose’ animations to lush multi-plane technicolor films with animated characters conveying personality and eliciting emotion.

I’m also inspired by the work of Pixar—and am amazed at the analogous progress of 3D animation from the mid-80s to the mid-90s.

Together, Disney and Pixar have produced over 640 animated shorts and features.  With almost all films released on disc, I wanted to keep track of what I had seen.  I also wanted a quick, mobile reference with some basic information on each film.  And so, I built WALT so I could use it, and hopefully other Disney and Pixar animation fans would enjoy using it as well.

Focus on framework and fundamentals

The features of my desired app lined up nicely with the core functionality common in most iOS apps: present a set of data, navigate, search, sort, filter, summarize.

Since this first app is relatively straightforward, I wanted to take the stock iOS frameworks out for a spin and see just how far they would take me, and how quickly.

My past experience in working with Cocoa frameworks is that it is good practice to use the highest level of abstraction that meets your needs.  On the flip side, it can be treacherous to try to force a framework to be something that it isn’t.  If you must twist the higher abstraction into a pretzel knot of ugly bad practices to meet your needs, it’s usually best to drop to a lower layer and implement what you need (and, of course, file enhancement requests so that a future release might meet your needs).

In the spirit of using the highest level of abstraction available, WALT uses standard UIKit controllers and views, with a handful of custom views.  The app hits just about all of the UIKit highlights: tabs, navigation, tables, custom table cells, search results, modal view controllers.

For data management, WALT uses Core Data.  A key consideration in this choice was the ability to do incremental fetches and updates. It had also been a while since I had done Core Data development on the Mac, so it was a good opportunity to reacquaint myself.

I tend to exercise caution when adopting version 1.0 technologies and frameworks – especially if an app will be immediately pushing the limits of the new technology.  In WALT, I adopted two relatively new technologies: Storyboards and Automatic Reference Counting (ARC).

Since I planned to support iOS 5.0 and later, the straightforward layout of the app made Storyboards seem to be a very good fit.  The Storyboard abstraction seemed useful and held promise to simplify development and eliminate code.  In the end, I encountered a severe accessibility bug which required much of the eliminated code to be added back in one form or another.  Overall using Storyboards was a slight net win for the project, but the experience definitely reinforced my attitude of caution.

I had no intention of moving to ARC, but a few conversations with former Apple colleagues led me to give it a try on a development branch.  It went without a hitch.  My project was in an early stage, so there was not much code to convert, and I did not anticipate writing much code that would hit known ARC ‘sore spots’, such as frequent conversions between Core Foundation and Cocoa.  So, I merged the ARC changes into my main development branch and have not regretted doing so at all.

Finally, I was able to add a bit of social media support in WALT very quickly.  The Twitter framework in iOS 5 made it trivial to add support for tweeting about films you have watched.

Build a solid, speedy, polished app

These three, of course, should be goals of any app since they map to quality, performance, and user experience.  They can also be somewhat subjective areas – how responsive is responsive enough?  How polished is polished enough?  In these areas, I set some specific goals and worked towards them.

First, for me, solid means not crashing, not losing data and not leaking memory.  I don’t have enough programmer hubris to claim there are no bugs in the app, but hours of use without an issue made the app feel pretty solid to me and my testers.  Instruments came in handy to check for leaks—ARC seemed to live up to its promise.  I also periodically ran the Xcode static analyzer, which is excellent at catching potential problems.

Being speedy meant focusing on app launch, scrolling, navigating and searching.  I focused on how responsive the app felt to the user rather than using timed code benchmarks.  The criteria were that the app should launch within a count of ‘One-Mississippi Two-Mississippi’; scrolling should not lag—even in longer lists; navigation should be smooth and search results should feel immediate.

I did hit some scrolling performance issues and found Instruments to be a valuable tool in tracking down the problems.  Most turned out to be cases where I was unnecessarily fetching data repeatedly, which the Core Data instruments made very obvious.  In the end, the app feels snappy.

Making an app ‘polished’ is perhaps the most subjective and all-encompassing—it includes everything from user interaction, to the look of the app; from providing retina graphics, to how the app behaves for VoiceOver users.  Since I was already on the road of seeing just how far the stock frameworks would take me, much of the app polish is provided by the frameworks themselves.

But even leveraging what the frameworks have to offer, there are lots of subtle things that make an app feel better.  WALT contains touches like adjusting the display of a table index and sections depending on the contents of a list, restoring the user’s previous navigation and scrolling state if the app is terminated, and minor but important things like making the tap target size on a checkbox larger than its graphic.

Polishing the app was a process of using the app as I developed it, refining the behavior as the app evolved.  With polish, often the code you end up writing is not particularly difficult or time-consuming, the bulk of the work is in determining exactly how an app should behave.

Ship It

When working on a large project like OS X, someone else sets the schedule and decides that the product is ready to ship.  As an independent, those decisions are yours, but so is the responsibility for those decisions.

I found one of the keys to shipping was to guard against feature creep by choosing a core set of features for version 1.0 and focusing on them.  The process was fairly lightweight.  First, record every issue, enhancement and feature idea.  Then, decide which items absolutely need to be in the 1.0 milestone and move the rest to a Later milestone.

The lists were not immutable.  As work progressed, it became clear some things were more important than originally thought, and some things less so.  I periodically revisited the lists of issues and as time went by became more and more hard-nosed about which features remained in 1.0.

Another hurdle was deciding when the app is ready to ship.  In addition to the feature set, there is so much that can be tweaked—the exact wording of labels, the default layout of tabs, which icons represent which tabs, tweaks to graphics.  At a certain point, I needed to stop tweaking and send the app out into the world.

Wrap Up

I am very excited to have released this first app, WALT, both as a user and as a developer.

As a user of WALT, I’m currently tracking my progress through the Silly Symphony film series where the Disney studio tested many of the animation technologies and techniques that were used in the first animated feature film Snow White.

As a developer, each project brings new learning and insights and this one was no exception.  But, there is also no denying it is a very good feeling when something you have created is released into the world.

And for beginner iOS developers taking those first steps towards building an app, I invite you to join me as I lead a Full Day iOS Tutorial at CocoaConf in Portland, OR on October 25th.

I welcome your comments on this or any other blog post, or the app WALT itself at comments@jamesdempsey.net.

Moving to new Objective-C literals

Last week, I moved one of my projects over to use the new Objective-C literals for NSNumber, NSArray and NSDictionary as well as the new syntax for boxing expressions into NSNumber and NSString objects.  Here are a few comments on how it went.

In a nutshell, the new language features allow code like [NSNumber numberWithInteger:7] to be replaced by a more compact syntax like @7.  The LLVM site has a full description of the new syntax and usage.

Refactoring in Xcode

I ran the ‘Convert To Modern Objective-C Syntax’ refactoring tool in Xcode 4.4 to do the conversion.  There is no option to choose which modern syntax features you wish to adopt before the conversion begins, but in Xcode 4.4, you can choose diff by diff which changes you wish to keep.  Xcode seems to think my Objective-C syntax is already modern enough in other respects, since the only changes the refactoring made was to move to Obj-C literals.

Even if you want to introduce the new literals into your code manually, you might find it interesting to run the refactoring tool just to browse through its proposed changes.  You can cancel the changes rather than apply them, but just by reviewing the proposed diffs, you might see use cases for Obj-C literals that may not have occurred to you.

There were more places than I was expecting where my code creates NSNumber instances from integer expressions and enums.  Also, before running the refactoring tool, it had not occurred to me how often the expression being boxed would just be a single variable holding a scalar value.

So there were numerous places where the updated code did something similar to the following:

NSInteger myValue = 0;
// Do some stuff that changes myValue
[myArray addObject:@(myValue)];

Before doing the conversion on my own code, I definitely underappreciated the power and usefulness of boxed expressions.

Overall, I found the refactoring in Xcode 4.4. to be easy and straightforward.  The Xcode project I converted was a medium-sized Objective-C project using the iOS 5.1 SDK.

A crashing issue to watch out for

There is a potential crashing issue that I ran into that you should be aware of.  In one spot, the code was relying on a nil value terminating the list of arguments to create an array.  So something like:

-useRequiredValue:(id)requiredObj optionalValue:(id)optionalObj {
   NSArray *array = 
         [NSArray arrayWithObjects:requiredObj, optionalObj, nil];
   // Do something useful with array
}

The method +arrayWithObjects: stops processing arguments when it hits the first nil argument.  The code above relies on that fact to create a one-object array when optionalObj is nil, and a two-object array when optionalObj exists.

The conversion changed the array creation to:

NSArray *array = @[requiredObj, optionalObj];

With array literals, relying on a terminating nil does not work.  Behind the scenes, a literal NSArray is created with +arrayWithObjects:count: which requires non-nil values.  So, after converting to Obj-C literals, an exception was thrown whenever the optional argument was nil.

A similar issue arises with dictionaries if you are relying on early nil-termination with +dictionaryWithObjectsAndKeys: and then move to use an NSDictionary literal.

Note that this issue is not flagged by the refactoring tool or the static analyzer, so it is something to watch out for when converting to use Objective-C literals.

To address the issue, the options are either to revert to the previous code or change the code to something else.

I changed the code to something like this:

NSArray *array =
     (optionalObj != nil) ? @[requiredObj, optionalObj] : @[requiredObj];

I decided to stop using the ‘early nil’ approach for a few reasons.  The first is that I know I will be tempted to change that line of code to use a literal array every time I look at it, which will reintroduce the crash.  I could guard against that by adding an emphatic comment explaining the situation, to prevent myself from changing that line of code.  So, everytime I look at the code, I’ll be tempted to convert the line of code, and then have to read about why I can’t convert that line of code.

It seemed more maintainable to move to a different way of doing the same thing.  In addition, the new code is less tricky/clever than the old code and more explicit about what the result will be; no comment is needed.  I am a big fan of well-commented code, but I’m also a fan of code that is obvious enough that the code can clearly speak for itself.

Readability

In most cases, the new literal syntax leads to code that is both more compact and more readable.  In the above example, the new code is not much more compact than the original code, but, the intent is clearer, which I find more readable.  With numbers, ‘@3.6’ is more readable and compact than ‘[NSNumber numberWithFloat:3.6]’.  For arrays and dictionaries, I find that my eyes are not yet parsing literal array and dictionary creation as smoothly as more familiar Objective-C code, but I suspect that will change as I use and see the new constructs more often.

Boxed expressions may cause readability to suffer to some degree.  When you encounter something like ‘@(someVariable)’, it is ambiguous whether you are creating a number or string.  You need to know the type of the expression to know for certain.  In practice, descriptive variable names should make this less of an issue.

I find the code becomes less readable as the boxed expression inside the parenthesis becomes more complex and when Objective-C literals and boxed expressions are combined and nested.  We are moving from code that had giant billboards of [NSString stringWith…  or [NSNumber numberWith… proclaiming just what is being creating, to code which is definitely more compact, but may also be more difficult to read.  For example:

@{ValueAverage : @((self.currentValue - [self.relatedObject
currentValue]) / 2), BoundaryValues : @[@(self.maxValue), @(self.minValue)]}

Even after such a short time using the new syntax, I find that on balance, the literals and boxed expressions make for more readable and compact code, with a few cases where readability seems to suffer a bit.  It is also early in the game, in terms of using the new syntax on a day to day basis.  My opinion will continue to evolve as I use the new constructs more.

Backward binary compatibility and a little BOOL wrinkle

The new Objective-C literal functionality is handled in the compiler, so you must be using a recent version of the LLVM compiler to use this syntax.  Since the compiler generates code that uses existing object creation methods, the code you write and compile can run on previous versions of OS X or iOS.

So, even if you are targeting releases of your app on OS versions earlier than OS X 10.8, or iOS 6, you can use the new literals along with the new version of the tools.

I did encounter one wrinkle with BOOL literals.  To enable the compiler to properly deal with @YES and @NO to represent NSNumber BOOL values, two new language keywords were added to the Objective-C headers.

The iOS 5.1 SDK does not include these new keywords.  So, if you are using Xcode 4.4 and the iOS 5.1 SDK, you need to ‘box’ the BOOL values as expressions to compile: @(YES) and @(NO).

The Mountain Lion 10.8 SDK does have the new keywords as will SDKs moving forward, so this is just a minor transitional oddity.

Conclusion

Overall, I think the new Objective-C literals and boxed expressions are an excellent addition to the language.  I found converting to the new syntax using Xcode 4.4 refactoring to be a smooth process.  The only real bump I hit was in code where I was relying on early nil termination of arguments when creating an array.  The other wrinkle I encountered was the need to box BOOL values using Xcode 4.4. and the iOS 5.1 SDK.   I’m looking forward to using these new language features moving forward.