dependency graphs

Dependency graphs are a great way to visualize the complexity of a project and discover ways to simplify it. I've been looking for something to use on Xcode projects for a while without success, until I stumbled on this Python script. It generates a .dot file that imports into OmniGraffle without a problem. Great stuff.

StringyString part II

A fully automated alternative to STRINGIFY(StringString).

Select your app target in the Project Navigator and select Build Phases.

Click + to create a new Run Script item phase and drag it immediately below the top Target Dependencies item.

Set the shell to /usr/bin/ruby.

Copy the following into the text area:

strings_path = "#{ENV['SRCROOT']}/path/to/en.lproj/Localizable.strings"  
header_path = "#{ENV['SRCROOT']}/path/to/LocalizableStrings.h"  
regexp = %r{  
    ^     # start of line
    \s*    # optional whitespace
    "      # open quote
    (\w+)  # captured identifier
    "      # close quote
    \s*    # optional whitespace
    =      # =
    \s*    # optional whitespace
    "      # open quote
    .*     # any text
    "      # close quote
    \s*    # optional whitepace
    ;      # semi-colon
}x

if !File.exist?(header_path) ||  
   File.new(header_path).stat.mtime < File.new(strings_path).stat.mtime then
  File.open(header_path, "w") do |f|
    IO.readlines(strings_path).each do |line|
      match = regexp.match(line)
      if match then
        f.write("static NSString *#{match[1]} = @\"#{match[1]}\";\n")
      end
    end
  end
end  

Edit strings_path to point to the source Localizable.strings file.

Edit header_path to point to the desired destination header file.

Add the destination header file to your precompiled (".pch") header file.

When the project is built, the header file will be written if it's missing or out of date. For each string in the .strings file

"FooString" = "Hello Foo";

the header file will contain a corresponding string definition

static NSString *FooString = @"FooString";

STRINGIFY(StringyString)

You have a bunch of strings defined in Localizable.strings.

"FooString" = "Have a Foo.";  
"BarString" = "Anyone for Bar?";  
"BazString" = "Please pass the Baz.";  

You don't want to pass a string literal to NSLocalizedString() because (1) it's easy to make a typo, and (2) it's tedious since autocomplete can't assist.

NSArray *strings = @[  
  NSLocalizedString(@"FooString", nil),
  NSLocalizedString(@"BarStrung", nil),  // Oops.
  NSLocalizedString(@"BazString", nil)
];

You create a bunch of string constants instead.

static NSString *FooString = @"FooString";  
static NSString *BarString = @"BarString";  
static NSString *BazString = @"BazString";  

Now you get autocomplete, and if you fat-finger anyway, the compiler flags it and Xcode is eager to fix it for you.

But you still have to type

static NSString *BlaBlaBlaBla = @"BlaBlaBlaBla";  

when a string is added to Localized.strings, which is still tedious and typo-prone.

Fortunately, there is no tedious task that can't be simultaneously shortened and obfuscated by the judicious application of preprocessor macros.

#pragma mark Localizable string keys

#define _STRINGIFY(s) #s
#define STRINGIFY(s) _STRINGIFY(s)
#define $(x) *x = @STRINGIFY(x)

static NSString $(FooString);  
static NSString $(BarString);  
static NSString $(BazString);  
... etc ...

#undef _STRINGIFY
#undef STRINGIF
#undef $

And yes, I probably could have written a build script to autogenerate the string definitions from Localizable.strings in less time than it took to write this.

snippet edit

Seven years or so later, Snippet Edit makes Xcode's snippets finally as useful as Completion Dictionary was.

nag screens

1

2

3

I particularly dislike the little disclosure triangle that stays hidden until you hover over the Later button, misleading you into thinking you can just make the whole thing go away with one click. Oh no.