Wild Schemes

While I work with them every day, I never really understood Xcode 4 schemes until recently.1

What changed was that I listened to Apple’s WWDC 2012 session 408 “Working with Schemes and Projects in Xcode”, presented in part by the esteemed @rballard, which explains them very well and in great detail.

Now, I can’t give you a link to this session, because it’s hidden behind an ADC login. You have to go to https://developer.apple.com/videos/wwdc/2012/, sign in, and search for the title. This, for me, is an argument for Apple to release its WWDC sessions to the public, at least the older ones, with permanent links and searchable keywords, because this one session alone would banish the frustration and confusion of many developers wrestling with Xcode 4.

In Xcode 3, in general, you had a combination of simple actions (the Build action, the Run action), and build configurations (the Debug build configuration, the Release build configuration). You couldn’t make new actions, you could only make new build configurations — useful if you wanted, say, to make two different kind of release builds. You’d switch to the desired configuration, and then perform the action.

In Xcode 4, you can still make new build configurations if you wish (Project editor view → project → Info tab), but you can’t switch between them directly from the UI like you could in Xcode 3. Instead, you switch between schemes.

Schemes are built on top of all the older infrastructure: projects, targets, and build configurations. They can’t work without it, but they’re really an entirely different beast.

Every scheme has five predefined actions: Run, Test, Profile, Analyze, and Archive. Make a new scheme? Get five actions. Make a second scheme? Get five more actions.

More accurately, you get a bunch of settings for those five actions. To see them for yourself, choose your scheme name and Edit Scheme… from the Xcode 4 toolbar, like so:

Menu to Edit a Scheme.png

and you’ll see the scheme-editing sheet. There, select Run:

Editing a Scheme.png

The first setting? The build configuration to use. That’s how the old and the new are tied together. The Run, Test, and Analyze actions by default use the Debug configuration, while Profile and Archive use the Release configuration. I can see the logic in it, but it takes a little while to get your head around it. Xcode 4 is saying, “We don’t expect you to switch back and forth between build configurations. Just use the right action for the job, and we’ll take care of which build configuration to use for you.” (Well, if it could talk.2)

Hey, hey, hey: didn’t you forget something? What about building?

Turns out, building isn’t one of the scheme actions. That’s because it’s a prerequisite of each of the five actions. Want to run? Gotta build first. Want to Analyze? Gotta build first.

One neat thing about schemes is that, for each action, you can choose what to build:

Scheme Build.png

When you first create an app project, and you configure Xcode to create a test target in addition to an app target, it sets up your default app scheme such that, for the Test action, it builds both your test target and your app target. You don’t need an extra test target scheme.

(I actually go further, and check the test target checkbox for the Run scheme action too, so I know immediately if code changes break my test build.)

But for every target you create after that, by default Xcode auto-creates a new, separate scheme for it. That…works, I guess, but I prefer to take advantage of the configurable build stage to ensure I only need one scheme per project.

Let’s say, in my sample project, I make a new MyLibrary target. This creates a MyLibrary scheme, which I don’t want. So I go to manage my schemes:

Menu to Manage Schemes.png

And I delete the MyLibrary scheme using the minus button:

Delete Scheme.png

(Note: this scheme-managing sheet is also where you can turn off the preference to auto-generate schemes.)

Then I go back to the build view of my remaining scheme and add in the MyLibrary target using the plus button:

Add Target to Scheme.png

Now, a library isn’t the best example here, because you normally want to set a build dependency between the library target and the app target, and that dependency will ensure the library gets built regardless of whether it’s explicitly part of the scheme. A better example would be an entirely separate binary, like a plugin or a command-line tool. But I think you get the picture.

Two more points:

First, things get more complicated if you want additional build configurations, such as a “Debug with Special Profiling” or “Release for Beta-Testers”. Because the build configurations are built into the scheme actions, if you want to switch build configurations, you either need to hand-edit your scheme each time before you build (blech) or make a new scheme for the new build configuration — and get your five new actions, even if, for example, you only need to duplicate the Run action, or the Archive action. Neither solution is ideal.

My opinion is, if you’re going to bake build configurations into actions, allow users to create new custom actions for a scheme.

Second, have a look at the Xcode 4 Product menu:

Product Menu.png

You’ll see menu items for five scheme actions, Run, Test, Profile, Analyze, and Archive, at the top. Good so far.

But there’s also a Build menu item. What’s up with that?

And even more curious, there are also separate “Build” menu items for almost all the actions in their own submenu:

Build for Menu.png

and “Without Building” menu items for some of the actions in their own submenu:

Without Building Menu.png

So even though conceptually a scheme fuses a build pre-action with an action — that’s their whole point — Xcode 4 pretty much gives you every opportunity to invoke those parts of a scheme separately.

The fact that Xcode provides this much flexibility is very good, but I have to think — if breaking with the original concept is so important that there are eight menu items to work around it…perhaps the original concept needs some tweaking?

(Also, if you were wondering: the Build menu item is the same as “Build for Running”.)

 

1. Which is unfortunate, because I was working on Xcode (albeit a different part of it) when they were invented.

2. Which it can: http://www.textfromxcode.com/

App Chowder

When I complain about this or that inadequacy in Xcode, there’s always a small but persistent chorus singing the praises of JetBrains’s Cocoa IDE AppCode.1

I’m planning on trying it2, but before I do, I figured I’d lay down some “claim chowder”<tm Gruber> explaining my doubts.

Heinz 57
The trouble with any company attempting to insert itself in another company’s value chain is that they’re playing a constant, unsuccessful game of catchup. GnuStep, anyone? Mono? Every new version is a chance for things to break, every new feature is something else to fall behind on.

My guess is that AppCode won’t support a lot of the things that are built in to Xcode, from minor editing conveniences to essential features. I already know you can’t edit xibs in it, though their promo page claims that refactoring will work in xibs and storyboards, which would be impressive.

I suspect there will be at least one dealbreaker in this category. And by “dealbreaker” I mean, something I have to go back to Xcode for often enough that I might as well just keep using Xcode.

Foreign Exchange
While the screenshot on their promo page doesn’t look that bad, I wonder if I won’t be able to stand their non-native UI. (One thing that Xcode has going for it now is that at least it looks good.) I’m assuming AppCode, like their Java IDE IntelliJ IDEA, is written in Java, not native Objective-C, and that’s why it doesn’t use standard controls; its UI drawing is meant to be cross-platform. So there may also be UI behaviors that feel alien and throw me off.

If the UI doesn’t do the trick, speed issues might. The Java runtime on the Mac was never known for its speed. On the other hand, Xcode itself can be slow and laggy at times, so it will be interesting to see where AppCode lands in comparison. I won’t be trying it with any massive projects, so that’s good (might work better) and bad (won’t be able to judge how it handles them).

THIS One Goes There, THAT One Goes There
I’m also worried about integration. Much of the reason Xcode used to be so bad was that it couldn’t link against gcc directly. Clang is now deeply integrated into Xcode, including its index and its code editor. Does AppCode have to do double the work to get the same result? Or does it try to parse the source code in its own, not-quite-matching way, leading to weird inconsistencies? Will builds be as fast? Will there be cryptic errors when I try something nobody thought of to integrate properly? Can I really trust it to edit project and workspace files (whose formats are undocumented)?

To some degree, this is unfair. Xcode itself it full of weird bugs; expecting AppCode to be perfect is holding it to a different standard.

But I’d rather not have bugs on top of my bugs.

One Is the Loneliest Number
My final concern is that, even if I get everything working properly, even if my productivity skyrockets, I’m still going to be off doing something different than 99.9% of the Cocoa engineers out there. It’s going to take its toll mentally. It’s ironic that I would say this for a platform whose motto once was “Think Different”, but there you go.

But if I start now, at least I’ll have something to talk about for the podcast next week!

 

1. Still requires Xcode to be installed, because it uses the Xcode command line tool xcodebuild to build.

2. $99, but a 30-day trial period, which is smart.

Autorelease the Hounds

I’ve been thinking about weak references in ARC.

Let’s say I’ve got a new application model layer, for describing urban transportation infrastructure:

Diagram of a tree of objects

The topmost object is an “Urban Transportation” object, which owns a list of city objects (in this case, “New York” and “San Francisco”). Under each city object are the kinds of transportation in that city, such as cars, buses, and airports (planes). Each object owns the things underneath it and is responsible for disposing of them (releasing them) when they’re removed. Good so far.

The odd man out in the diagram above is the flight object, “Flight 2077”. It’s owned directly by the top-level object, for lack of anyone else to provide direct ownership. But the two airports underneath the “New York” and “San Francisco” objects each have what I’ll call a “cross-tree” reference to it, since it’s a flight between those two cities.

What should those cross-tree references look like?

In retain/release, you have two options, and they’re both bad. You could use unretained pointers. Those have the advantage of not creating retain cycles, but they’re dangerous because they’ll continue to refer to the flight object even after it’s been released.

Or you could use retained pointers and add what I’ll call an “invalidate” method to the flight. “invalidate” methods rely on the fact that, even when the retain cycle hasn’t been broken yet, you know from your application’s logic when your object should go away. At that time, you call “invalidate” on it, and then it breaks the cycles for you. In this case, the flight object would have to contain its own pointers back to the referring airports, and be able to tell the airports to remove their pointers/retains to itself. The more classes in your model that can have cross-tree pointers, the more “invalidate” methods you need, to the point where you’d probably just want to make it a mandatory protocol. Lots of extra logic, easy to get wrong.

In garbage collection, you’ve got a better option. You can use weak pointers. That way, once all the strong references are removed (in this case, the reference from the “Urban Transportation” object), the weak references in the airport objects would be set to nil, and the flight object would be deallocated. Easy peasy. You could still use an “invalidate” method here, but it’s not necessary. (Though it’s still a good idea for releasing other sorts of resources under GC without employing a finalize method, which Apple discourages.)

In ARC, you can also use weak pointers, in exactly the same way you use them in GC. But is that an undesirable amount of overhead? The whole point of ARC is that it’s fast and immediate. But the more weak pointers you use, the more you’re forcing the system to keep track of all your pointers and objects, in a way very reminiscent of GC, and not at all like the rest of ARC, which just drops in just the right number of invisible retains and releases at compile time and calls it a day.

My gut feeling here, though, is that the overhead won’t be that much unless you’ve got an overwhelmingly large number of objects. Won’t know till I try it!

7/15/2012 Note: The title is actually taken from one of the rejected episode titles of the latest Edge Cases episode, which we recorded before I posted this, even though the episode itself was only released afterwards. Wolf’s thoughts definitely informed my final edit of this post. That title actually came from Vincent Gable’s tweet, however.

Put It on My Tab

In the Edge Cases1 episode “My First Xcode 4 Rant”, I talk about my frustrations dealing with the Xcode 4 window.

Apple has a response, kind of, in their WWDC 2012 video “Working Efficiently with Xcode”.2 There, (amid a lot of other helpful tips) they talk about “task-based tabs”. To get around the fact that Xcode makes no effort to configure its helper views for the kind of document you’re editing, they recommend manually creating customized layouts in their own tabs: a design tab for xibs, for example, where the utility pane is always present.

I’ve used Xcode 4’s tabs, and they don’t work for me.

For one, it’s clunky to have to make sure you only open certain documents in certain kinds of tabs. I want to switch between documents quickly. This usually means using the filter in the navigator pane to find the right file, then selecting it there, then using file history to go back and forth between that file and my previous files. All good stuff. But if I have to intermix that with, OK, now, you need to remember to go over here to open this file, well: by the time I remember to do that, I might as well just use the keystroke shortcuts to open and close the utility pane each time, no?

But the worst is Command-W.

You know what happens, after you lovingly spend minutes and minutes configuring your special tab with exactly the right spacing and exactly the right helper panes? That’s right, you accidentally hit Command-W, because your fingers have been trained for years to dismiss files this way. And when you do that, all your painstaking work is gone in an instant. Poof! (Actually, there’s no animation.) No undo. It’s just gone.

After doing that enough times, you stop spending a lot of effort configuring tabs.

But you still don’t escape, because even if you just have one working tab, you inevitably start arranging that tab how you want it to look. And if you have a second tab of any sort, such as the Debug tab that’s recommended in the session so that you don’t have to keep showing and hiding the debugger pane, Command-W kills your first tab dead.

And if you have no tabs, Xcode 4 closes your whole window, so you can’t even get used to one shortcut for views and one shortcut for windows. Frustrating.

And even if all that didn’t bother you that much, remember Rule Number 4 from “Would They Call It iCode?”: No UI Fiddling! Needing to manually set up your own tabs is the definition of UI fiddling. It’s as if Apple is saying, Go on, live in the trees, we recommend it! But instead of providing you with a treehouse, they just provide you with a bunch of lumber and nails. (And your house falls apart if you close the door too hard.)

Edit: OpenRadar 1814402

Edit 2: Inspired by Ole Begemann’s comment, I found this post by Brian Webster describing how he used Xcode custom behaviors (with keyboard shortcuts) to create custom tabs. Lots of good details. Don’t have time to address this in depth right now, but wanted to get it out there.

Final edit: Imagine every time you open a new file, Xcode’s window autoconfigures itself to the default layout for such a file (which you can change in prefs). For xib files, it shows the utility pane; for .m files, it hides the utility pane, but shows the assistant pane with the associated .h file. And further: it remembers any custom configuration for individual files, so if the last time you had Foo.m open, you also had its associated xib file open, it replicates that as well.

Custom actions can be very powerful. If I’d actually finished watching the “Working Efficiently” session before I wrote this post, I would have found out exactly how to set them up. They can “fix up” the layout options to an existing file, for example; you don’t need to create a new tab. Such things can help me out quite a bit.

But I still think it’s doubling down on the wrong approach. Too many extra steps you’ll have to do over and over, too much manual configuration of things Xcode should already know. And that’s all I have to say on the matter for now.

 

1. Note: despite the URL, which we chose because it would be laughable to expect “EdgeCases” to be available in 2012, the show is called “Edge Cases”, not “The Edge Cases Show”. Like, y’know, “Bono”, or something.

2. No direct link, but go to https://developer.apple.com/videos/wwdc/2012/, sign in, and search for the title. And if the website verbiage and my Twitter compatriots are to be trusted, you don’t even need a paid account to watch it.

How Long

I made a chart showing how long it would take, at current growth rates1, for Apple to sell an iPhone to every man, woman, and child on the planet:

Chart showing Apple iPhone exponential sales growth

Per Wikipedia’s estimate of world population (about 7 billion), it would take about five years.

(Uses information from the post “iPad versus iPhone versus iPod” from AAPLinvestors. PDF file here, table data as PDF here.)

 

1. Using HUUUUUGE amounts of approximations and simplifications, I know. Assumes projected growth rate of 20%, which from the data for the past several years, seems like a decent assumption.