Chapter 23. Customizing Xcode

WHAT'S IN THIS CHAPTER?

  • Setting expert preferences

  • Changing command key shortcuts and editor key bindings

  • Using alternate editor applications

  • Creating custom text macros and command scripts

  • Making your own project, file, and target templates

Apple is famous for developing spare and elegant software. Rare indeed is the Apple application that suffers from "featurosis" — a malady of ever-expanding specialized features that eventually smother an application in an incomprehensible maze of commands and options.

Developers, however, are not consumers. At least they don't think of themselves as consumers. Developers are professionals that expect, nay demand, that almost every aspect of the tools they use be under their control to alter, repurpose, and tweak as they see fit. I have personally worked with a developer who, dissatisfied with the warnings produced by a compiler, downloaded its source code, corrected the perceived flaw, and built his own personalized version to use. Although the wisdom of his actions are debatable, the spirit of "if you don't like the way it works, build your own" runs deep and strong through the developer community.

For this reason, Xcode is a departure from most software produced by the engineering teams at Apple. Xcode has a dizzying array of customizable options, as witnessed by the monstrous Xcode Preferences window. Using the Xcode interface, you can completely customize the keystrokes used to invoke every Xcode command and motion. The extensive set of build settings enable you to specify any of the innumerable switches passed to compilers, linkers, and other tools employed by Xcode. You can completely reorganize the build process, and even assume complete responsibility for it. You are free to use a different text editor. You can even alter seemingly inconsequential interface details, such as the highlight colors used by the debugger.

Surprisingly, the list doesn't stop there. There are scores of hidden and undocumented customizations in Xcode. Most are application settings that you can alter by editing the Xcode preferences file, as discussed in this chapter. You can also create your own templates and customize the Xcode application by adding your own commands.

Warning

Most undocumented features are unsupported by Apple and the Xcode development team. I've tested every customization presented in this chapter, but that doesn't mean they will work in future versions of Xcode. Customization features that turn out to be popular are often re-implemented and appear in future versions in a friendlier and better-supported form. Check the release notes for the feature you are looking for.

XCODE PREFERENCES

If you came to this chapter looking for the meaning of a particular Xcode preference setting, most of them are discussed in the chapter that the setting applies to. For example, the options in the Code Sense tab are discussed in the "Code Sense" section in Chapter 7. To point you in the right direction, the following table lists the tabs in the Xcode Preferences window and the chapter where those settings are explained.

PREFERENCE TAB

CHAPTER

General

3 and 6

Code Sense

7

Building

17

Distributed Builds

17

Debugging

18

Key Bindings

23

Text Editing

6

Font & Colors

6

Indentation

6

File Types

6

Opening Quickly

6

Source Trees

21

SCM

21

Documentation

12

The Xcode documentation also describes the preference settings for each topic. Chapter 12 of this book provides assistance in browsing the Xcode Help documents.

KEY BINDINGS

The only preference tab not covered in the other chapters is Key Bindings. The Key Bindings pane, shown in Figure 23-1, allows you define the keystroke combination associated with just about every command and action that Xcode performs. You will also see that a few actions are inaccessible in Xcode's default configuration and can only be accessed by assigning them a key binding.

FIGURE 23-1

Figure 23-1. FIGURE 23-1

Key bindings are stored in sets. Use the Key Binding Sets pop-up menu to switch between any of the predefined sets that ship with Xcode, or any sets that you've defined. Choose a set and click the Apply or OK button, and the new bindings take effect immediately. If you want to create a new binding set, select a base set and click the Duplicate button. Give the key binding set a name, as shown in Figure 23-2. To delete the selected set, click the Delete button. You cannot edit or delete predefined sets. To customize one of the predefined sets, duplicate it and edit the new set. If you try to edit a predefined key binding set, Xcode offers to first duplicate the set.

FIGURE 23-2

Figure 23-2. FIGURE 23-2

Menu Key Bindings

The Key Bindings pane has two tabs: Menu Key Bindings and Text Key Bindings. Menu key bindings, previously shown in Figure 23-1, bind keyboard shortcuts with the items in the Xcode menus. Menus items that are created dynamically, such as the list of recently opened projects, cannot be bound.

To edit a menu key binding, find the menu item in the hierarchy of menu groups and double-click its Key cell in the table. The menu item's current binding turns into an editable field, as shown in Figure 23-3. This is not a normal text edit field; it captures any single keystroke or combination you press. You've used it most recently in Chapter 22 when assigning keyboard shortcuts to actions.

To bind Command+P to the Xcode

Menu Key Bindings
FIGURE 23-3

Figure 23-3. FIGURE 23-3

To accept a key binding change, click outside the edit field. There is no keyboard shortcut to accept a binding, because any keystroke you press will be captured as the new binding. To remove a key binding, double-click the binding to enter edit mode and then click the grey − button to the right of the cell.

Traditionally, key bindings for menu items are Command key combinations. However, you are free to assign any keystroke combination you want. Existing examples are commands like Edit

FIGURE 23-3

Letter keys have no sense of case in key bindings. Command+A is one key combination, and Command+Shift+A is a different key combination. The state of the Caps Lock key has no influence on key bindings.

When editing key bindings, Xcode displays the symbols listed in the following table for special keys:

SYMBOL

KEY

˜CM

Command

˜OP

Option

˜SH

Shift

˜CT

Control

Left Arrow

Right Arrow

Up Arrow

Down Arrow

FIGURE 23-3

Tab

FIGURE 23-3

Esc

FIGURE 23-3

Delete (backspace)

FIGURE 23-3

Delete (forward)

FIGURE 23-3

Page Up

FIGURE 23-3

Page Down

FIGURE 23-3

Home

FIGURE 23-3

End

FIGURE 23-3

Return

FIGURE 23-3

Enter

Space

Space

Text Key Bindings

Text key bindings are the keystrokes that the text editor interprets as actions. By altering the text key bindings, you can change the behavior of the Xcode text editor.

Editing a text key binding is almost identical to the procedure for editing menu key bindings, as you can see in Figure 23-4. The only significant difference is that you can assign multiple key bindings to the same action. Any of the key combinations listed will invoke that action.

To alter a key binding, double-click the Keys field of the binding. Set the first, or replace the existing, key combination by typing the desired combination. To add additional key combinations to the same action, click the + button to the right side of the edit field, as shown in Figure 23-4. To delete a specific key combination, click the combination in the list to select it and then click the − button.

FIGURE 23-4

Figure 23-4. FIGURE 23-4

In the editor, any regular character key that is not assigned an action is inserted literally into the text — assuming the encoding of the document permits it.

To give you an idea of the kind of subtle change you can make to the editor, I'll step you through the process of turning off the automatic indenting that normally occurs when you press the Tab key.

Pressing the Tab key in most text editors simply inserts a tab character into the text. If you look at the key bindings for Xcode, you'll see that the Tab key is tied to the Insert Tab action. On the face of it, it would appear that Xcode does the same thing. In reality, Insert Tab adds an additional level of convenience; it automatically re-indents the line after inserting the tab.

The insert tab action that does nothing but insert a single tab character is named, cleverly, Insert Tab Without Extra Action. Normally, the automatic re-indenting of your text is a huge convenience, but you might find yourself in a situation where you just want a tab to be a tab. You can fix this in the key bindings for the editor, as follows:

  1. Open the Key Bindings tab of the Xcode Preferences. If you don't have your own key bindings set already, duplicate the current key bindings set and give it a new name.

  2. In the Text Key Bindings table, find the Insert Tab action and double-click its current binding (usually the Tab key).

  3. Hold down the Option key and press the Tab key. Click outside the field to set it.

  4. Click the OK button to adopt the new key bindings.

Editing the text key bindings changes the key combinations that Xcode will use in all text editor panes. In steps 2 and 3, you replaced the key binding for the Insert Tab action — the action with the extra features — with Option+Tab. Because the Option+Tab was previously bound to Insert Tab Without Extra Action, Xcode warned you of the conflict (see the bottom of Figure 23-4) and deleted that binding when you accepted the new one.

Now the Tab key (alone) has no binding. Without a special key binding, the Tab key is treated like any other character. No special reformatting or navigation is attached to inserting a single tab character any more. Alternatively, you could have accomplished the same thing by binding the Tab key to the Insert Tab Without Extra Action action. Because the Tab key represents the tab character, both are equivalent.

Key bindings are global, and both the menu key bindings and text key bindings share the same table. A key combination can only be assigned to a single menu command or editor action. Keep this in mind when you're assigning non-Command key combinations to menu commands and vice versa.

Key bindings are stored by name in your local ~/Library/Application Support/Xcode/Key Bindings folder as .pbxkeys files. You can exchange key binding files with other users. To install a key binding file, quit Xcode, copy a key bindings file into the Key Bindings folder, launch Xcode, and select the new set from the Key Bindings Sets pop-up menu.

USING AN EXTERNAL EDITOR

Is customizing the editor keystrokes and command shortcuts not enough? What if your favorite editing feature isn't included in Xcode's editors? What if Xcode doesn't even have an editor for the kind of file you've added? Fear not; you can elect to use a third-party editor, instead of the built-in editors provided by Xcode, for some or all of your editing needs.

Although Xcode's editors are powerful, Xcode has a relatively limited feature set when editing certain types of source files, like XML and HTML. The ability to plug a dedicated HTML editor into Xcode adds a powerful new dimension to your development environment.

Xcode has several different built-in editors. What you have been exposed to most in this book is the Source Code editor. There is also a Plain Text editor, an RTF (Rich Text File) editor, an XML Properties List editor, an Xcode Configurations Settings File editor, an AppleScript Dictionary editor, an Image editor, a Data Model editor, and a few others. The editor that is used when you open a file is determined by the settings in the File Types tab of the Xcode Preferences (see Chapter 6). Each file type that Xcode understands is associated with an editor. This can be one of the editors built into Xcode, an external application, or the decision can be deferred to the Finder.

Using an Alternate Editor Once

At any time, you can open a file using an alternate Xcode editor. Right/Control+click the source item and choose an editor from the Open As menu. The menu contains the list of Xcode's internal editors that are capable of editing that type of file. This is particularly useful when you need to edit or examine a file in a less structured way. For example, open a property list file (Info.plist) using the Plain Text editor if you want to edit the raw XML.

The Open With Finder command, found in the same menu, opens the file just as if you had opened the file in the Finder. For Xcode source files, it's very likely that this will be Xcode itself. To force a file to open in another application, use the Reveal In Finder command to locate the file. Drag the file to an application in the dock or open it using the Finder's Open With command. Alternatively, switch to the other application and use its File

Using an Alternate Editor Once

Specifying the Default Editor

Changing an editor choice in the preferences changes the default editor for that file type for all projects. For each file type, there are essentially four possibilities:

  • One of Xcode's built-in editors

  • A specific external editor

  • The default application the document is associated with in the operating system

  • The default for the super-type

In Figure 23-5, the default editor for AIFF audio files is being changed from its default — open it using the Finder, which would most likely open iTunes — to launch the Sound Studio audio editing application.

FIGURE 23-5

Figure 23-5. FIGURE 23-5

The choice of Xcode editors is always limited to the editors that understand that file type. A text.html file can be edited using the Source Code editor or the Plain Text editor; the RTF editor does not understand HTML and is not a choice. The text.rtf file type can be set to use the RTF editor, the Source Code editor, or the Plain Text editor.

File types are arranged in a hierarchy. When a type is set to Default, it defers the choice to its enclosing supertype. The top-level types have pre-programmed choices based on what editors are available for that type. In general, if you set the editor for a supertype and set a subtype to Default, the subtypes will use the editor of the supertype. In the example shown in Figure 23-5, the editor for the audio.aiff type is being set to Sound Studio. If the editor for the audio supertype had been set instead, all of the audio subtypes (audio.mp3, audio.au, audio.aiff, and so on) would open using Sound Studio.

Using an External Editor

When External Editor is specified, Xcode tells that application to open the file instead of opening it in a separate window. The selection of an external editor does not affect single-pane operations, such as simply selecting a source file in the project window. The editor pane in the project window, the class browser, the Project Find window, and others will continue to use Xcode's internal editor to immediately display a file in the same window. Only double-clicking a source item or using the Open in Separate Editor command opens the file in its external editor.

External editors are not synchronized to the changes made in Xcode. Whenever Xcode is reactivated, it checks to see if any files were altered. Xcode cannot detect changes made in an external program before they are saved. Get in the habit of saving all of your changes before switching back to Xcode. If it detects changes, Xcode rereads the file and updates its display.

In the unfortunate situation where changes have also been made in both the Xcode editor and the external application, the dialog box shown in Figure 23-6 is presented. You can choose to keep the changes made in Xcode, ignoring the changes that were written to disk, or vice versa. Xcode does not have a merge function and cannot combine the changes made in two places. If you have inadvertently made important changes in both places, use the Keep Xcode Version choice. Reopen the file in the external editor, and then copy and paste the changes made there to the editor pane in Xcode. Save the file within Xcode to commit the combined changes. Another approach is to use Xcode's File

Using an External Editor
FIGURE 23-6

Figure 23-6. FIGURE 23-6

Because of the hazard of editing source files in both the external editor and Xcode, I recommend using the Condensed layout or leaving the editing pane in the project window collapsed. This reduces the temptation to make a quick edit in a file that might be opened elsewhere.

Supported External Editors

Xcode provides full support for BBEdit, Text Wrangler, SubEthaEdit, Emacs, xemacs, and Interface Builder. Only those installed on your system will appear in the menu of external editors. You can also select any other application, but Xcode will only provide limited support for it.

Full support means that Xcode communicates with the external editor and tells it to save files when appropriate. For example, when you build a project, you can have Xcode tell your external editor to save all of the unsaved files before compiling, just as it does for internally edited files. Similarly, closing a project saves all files in an external editor.

External editors with limited support means once the file is open, Xcode has no more interaction with the application. It is your responsibility to save and close the file in the editor as needed.

External editors do not use the file and line encodings, tab, or indent settings of the source item. Some editors, like BBEdit, either automatically detect the format or store that information in the resource fork of the file. Use the external editor to change the encoding. Then change the settings in the project's source item to match.

TEXT MACROS

In the "Text Macros" section of Chapter 6, you learned how to insert text macros using the Edit

TEXT MACROS

This is the first of the non-sanctioned Xcode customizations. At one time, Apple supported user-created text macro files and appeared to be moving toward formally documenting them. Since then, Apple has retreated and now states that custom text macros are not (officially) supported. Nevertheless, creating your own isn't difficult and all currently available versions of Xcode will honor user-defined text macros.

Apple has made a recent concession to its "not supported" position. Xcode 3.2 has an XCCodeSenseFormattingOptions expert setting that allows you to override text macro values using your user defaults. The "Expert Preferences" section, later in this chapter, explains how to set Xcode's user defaults. The "Sanctioned Text Macro Customization" section describes what text macro properties you can redefine. But first, it helps if you understand how text macro definitions interact with one another.

Creating Text Macro Definitions

Adding or altering text macros is accomplished by defining your own text macro definitions. Definitions are written using a simple C-like structure syntax and stored in plain text files with an extension of .xctxtmacro. You can create new definitions, or replace any of the existing definitions that come bundled with Xcode.

When Xcode starts, it assembles all of the text macro definitions by searching two locations: the Xcode application bundle and the /Developer/Library/Xcode/Specifications folder. Xcode no longer looks in your home folder's ~/Library/Application Support/Xcode/Specifications folder, so it's no longer possible to install per-user custom text macros. Xcode scans both of the locations and reads every .xctxtmacro file that it finds. All of the .xctxtmacro files are read and digested before reading the files in the next location.

Note

If you've created a custom text macro file and your macros don't appear in Xcode, check the Console application. Xcode will record problems parsing an .xctxtmacro file in the system log.

Every definition contains an Identifier property. This property is the unique identifier for each definition. Definitions with a duplicate Identifier completely replace any previously read definition; Xcode essentially ignores any previously read definition with the same Identifier. Thus, definitions defined in your custom files can selectively override any definition found in the Xcode bundle.

Macro Definition

An .xctxtmacro file consists of a comma-separated list of definition blocks. The entire list is surrounded by parentheses, with each definition block contained between curly braces. Thus, the high-level structure of an .xctxtmacro file is:

(
    {
    definition
    },
    {
    definition
    },
    ...
)

A definition is an arbitrary collection of properties written as key/value pairs using the syntax key = value;. The value can be any text, a list, or another block of properties. A text value can be "naked" if it doesn't contain any special characters (such as whitespace) that would confuse the parser. If it does, it can be surrounded by quotes like a literal C string. Quoted strings can use the backslash to escape special characters.

Xcode defines a number of keys that have special meaning, some of which are required. You are free to add your own keys and use them as values in other definitions. The following shows an example definition:

{
    Identifier = c.printf;
    BasedOn = c;
    IsMenuItem = YES;
    Name = "Printf() Call";
    TextString = "printf("<#message#>");";
    CompletionPrefix = printf;
}

The .xctxtmacro file also supports C (/* ... */) and C++ (// ...) style comments.

Text Macro Properties

Each definition block must include an Identifier key. This key is used both to uniquely identify each definition and to refer to it in other definitions. By convention, it should be a reverse domain name that mirrors the hierarchy of the definition — this will make more sense when I explain about inheritance — and it should not contain any special characters that would require it to be quoted. Each Identifier is unique and a definition with a duplicate Identifier value will completely suppress any previously defined definition with the same Identifier.

The remaining properties of a definition shape its purpose. You can use any combination of properties you wish, but only certain combinations make any sense. To help you, the text macros built into Xcode contain a set of macros that expand to prototype definitions for new text macros — yes, they're macro macros. You can find these in the Edit

Text Macro Properties

The properties recognized by Xcode's text macro interpreter are listed in the following table:

MACRO PROPERTY

DESCRIPTION

Identifier

This is the unique identifier for the definition, and is a required property. The Identifier is not inherited.

Name

This is the descriptive name of the definition. For menu items, this is the name that will appear in the menu. When using Code Sense, the completion list will include this text as a description of the macro. The Name property is not inherited.

BasedOn

This property is the Identifier of the definition that this definition inherits. Most properties in the BasedOn definition are inherited; the exceptions are noted.

IsMenu

If set to YES. this definition creates a submenu in the Edit

Text Macro Properties

IsMenuItem

If set to YES, this text macro creates a menu item in the Edit

Text Macro Properties

TextString

The body of the text macro. This property defines the characters that will be inserted when the text macro is inserted. It can contain placeholders, described later, and variable references.

CompletionPrefix

This property is the "symbol" used to select the text macro using Code Sense. This is independent of the Name or TextString property of the text macro. For instance, a text macro that inserts a "do forever" loop could have a completion prefix of "forever". You could select this macro using code completion by typing as little as "fore". The CompletionPrefix property is not inherited.

IncludeContexts

A comma-separated list of the contexts where the definition is enabled. The root context, xcode, would enable the definition in any Xcode editor pane. The more specific context, xcode.lang.c, would enable the definition only when Xcode is editing a C or C-like language.

ExcludeContexts

A comma-separated list of the contexts where the definition should be disabled. Used to refine the IncludeContexts by excluding sub-contexts. For example, if IncludeContexts was ( xcode.lang.c ) and ExcludeContexts was ( xcode.lang.c.comment, xcode.lang.c.string ), then the definition would be active in all C source contexts, except when you're editing the text of a comment or in the middle of a literal string.

OnlyAtBOL

If set to YES, the macro will only appear as a Code Sense suggestion when the cursor position is at the beginning of a line.

CycleList

This property is a comma-separated list of definition identifiers. This list is used to order multiple text macros that share a single menu item. This allows you associate multiple variants of macro with a single menu item. The first time you choose the menu item, the first macro in the list is selected. If you immediately select the text macro menu item again, the first macro is removed and replaced with the second macro in the list. The list is circular and will continue to substitute the different variants until you settle on the one you want. This property does not control the order in which text macros appear in the completion list—or cycled using Control+.—when using Code Sense. Only the first definition should define a CycleList.

DefaultSettings

This property is a block of properties that define default property values for inherited definitions. Any definition that inherits from a definition containing a DefaultsSettings property will inherit all of the properties defined therein.

Placeholders

Including placeholders (<#name#>) in the TextString property makes it easy to jump immediately to the places in the macro that need to be replaced with valid code or other content. After a text macro is inserted, the first placeholder is automatically selected. Text macros support a special form of placeholder that surrounds the name of the placeholder with exclamation marks: <#!name!#>. If text in the editor pane is selected when the text macro is inserted, this special placeholder is automatically replaced with the selected text. If no text is selected, or the macro was inserted using code completion, the special placeholder acts like any other placeholder. A text macro can only include one special placeholder. If your text macro does not contain a special placeholder, and the user has text selected when the macro is inserted, the macro text replaces the original text.

Property References

Definitions can also include any other property name you wish to invent. Property names, either defined or inherited, can be inserted into any value using the syntax $(key), where key is the name of the property. In addition to any property values defined, you also have access to any of the macros listed in the "Template Macros" section, later in this chapter. For example, the reference $(FULLUSERNAME) will be replaced with the long account name of the current user.

The ability to define arbitrary property values, refer to property values in other properties, and inherit property values creates a flexible framework for designing modular text macro definitions. The following shows a simple example:

(
    {
        Identifier = james.main;
        TextString = "// main()
// Written by
$(FULLUSERNAME)
$(MainDecl)
{
	
	return (0);
}
";
        IsMenu = YES;
        Name = "James";
        ExcludeContexts = ( "xcode.lang.string", "xcode.lang.character",
                            "xcode.lang.comment", "xcode.lang.c.preprocessor" );
    },

        {
            Identifier = james.main.c;
            BasedOn = james.main;
            MainDecl = "int main( int argc, char** argv )";
            IncludeContexts = ( "xcode.lang.c" );
            OnlyAtBOL = YES;

            IsMenuItem = YES;
            Name = "main()";

            CompletionPrefix = main;
            CycleList = (
                james.main.c,
                james.main.java
                );
        },

        {
            Identifier = james.main.java;
            BasedOn = james.main;
            MainDecl = "public static int main( int argc, String[] argv )";
            IncludeContexts = ( "xcode.lang.java" );
            OnlyAtBOL = YES;
IsMenuItem = NO;
            Name = "main()";

            CompletionPrefix = main;
        }

)

In this example, the james.main definition creates the generic definition for a text macro that inserts an empty main() function. It also creates a submenu in the Edit

Property References

The TextString property refers to the (as yet undefined) MainDecl property. The two definitions that follow, james.main.c and james.main.java, define actual text macros that appear in the text macro menu and code completion. Both inherit the TextString property defined in james.main (via the BasedOn property). The individual variants — one for C and the other for Java — define the MainDecl property that will replace the $(MainDecl) reference when the TextString property is resolved.

The end result is two macros that appear in the text macro menu and code completion as "main()". When invoked while editing a C source file, it inserts a main() function using the declaration int main ( int argc, char ** argv ). When inserted in a Java source file, it emits the same function but has a declaration of public static int main( int argc, String[] argv ) instead.

You can find more complex examples of inheritance and property references in the .xctxtmacro files supplied with Xcode. Use the following Terminal command to ferret out the text macro files that come bundled with Xcode:

find /Developer -name '*.xctxtmacro'

Open these files in Xcode, or any text editor, to get a feel for how text macros are structured and organized. The text macros in Xcode are both elaborate and sophisticated, and because text macros are not officially explained anywhere, these files and the comments they contain are the closest thing you'll find to documentation.

Sanctioned Text Macro Customization

The text macros built into Xcode make extensive use of inherited properties that allow you to globally customize their formatting. For example, the TextString property defined by Xcode's try/catch macro looks like this:

TextString = "try$(BlockSeparator){
	<#!statements!#>
}$(PostBlockSeparator)
catch$(PreExpressionsSpacing)($(InExpressionsSpacing)<#exception#>
$(InExpressionsSpacing))$(BlockSeparator){
	<#handler#>
}
$(PostBlockSeparator)finally$(BlockSeparator){
	<#statements#>
}";

The reason the macro is so complex is because all of the macros included in Xcode use a number of variables to define common formatting elements, such as the spacing before and after an expression. By overriding selected properties, you can redefine the formatting of every Xcode text macro with just a few lines.

This used to be done by creating your own text macro definition files. But now that Apple has shied away from supporting per-user text macro definitions, one of the most common reasons to do so is also cut off. Apple has, instead, provided the same functionality via an expert preference. The "Expert Preferences" section explains how to define these special settings.

The expert preference setting that affects text macros is named XCCodeSenseFormattingOptions. The value is a dictionary — this is one setting that's easier to edit using the Properties List Editor, rather than via the command line. It can contain values for any, or all, of the following text macro property values:

PROPERTY

DEFAULT

DESCRIPTION

BlockSeparator

""

Whitespace after the parenthesized expression of an if, for, or while statement and its opening brace.

PostBlockSeparator

" "

Whitespace after the closing brace of a block.

FunctionBlockSeparator

" "

Whitespace after a method or function name and argument-list declarations and its body.

PreExpressionsSpacing

" "

Whitespace between an if, for, and while keyword and the opening parenthesis.

InFunctionArgsSpacing

""

Whitespace inside a parenthesized function argument list (after the opening parenthesis and the closing parenthesis).

InExpressionsSpacing

""

Whitespace inside a parenthesized expression (after the opening parenthesis and the closing parenthesis).

PreFunctionArgsSpacing

""

Whitespace between a function name and the opening parenthesis.

PreCommaSpacing

""

Whitespace before a comma inside a function argument list.

PostCommaSpacing

" "

Whitespace after a comma inside a function argument list.

PreMethodTypeSpacing

" "

Whitespace before the parenthesized return type in an Objective-C method declaration.

PreMethodDeclSpacing

" "

Whitespace between the parenthesized return type and the method name in an Objective-C method declaration.

InMessageSpacing

""

Whitespace inside an Objective-C message expression—after the opening bracket and before the closing bracket.

PreColonSpacing

""

Whitespace before a colon in an Objective-C method name or message expression.

PostColonSpacing

""

Whitespace after a colon in an Objective-C method name or message expression.

MessageArgSpacing

""

Whitespace between the parenthesized type and argument name in an Objective-C method declaration.

CaseStatementSpacing

" "

Relative indentation of a case keyword inside a switch block. A tab (' ') character indents by the tab indentation width.

For example, if you want all code blocks inserted by Xcode text macros to place the opening curly brace on the next line of code, redefine the BlockSeparator property to " ". The following command accomplishes this from the command line:

defaults write com.apple.Xcode XCCodeSenseFormattingOptions
-dict BlockSeparator '
'

This command creates a dictionary in the Xcode user properties with a single key/value pair of BlockSeparator = ' '. Note that this also replaces any previous dictionary that might have been set, another reason why using the Properties List Editor is a better idea.

EXPERT PREFERENCES

Expert Preferences are additional, often obscure, preferences for which there is no direct user interface for changing it. You set expert preferences by directly altering the settings in the preferences file for the Xcode application. This file is com.apple.Xcode.plist, located in your ~/Library/Preferences folder. There are two simple ways of changing these settings. However you alter them, remember that the Xcode application should not be running when you do. Changing some values while Xcode is running may have unpredictable consequences. It's best to first quit the Xcode application, make your changes, and then launch Xcode again.

The first method is to use the defaults command from a Terminal window. The syntax for setting a value in the Xcode preferences is as follows:

defaults write com.apple.Xcode key -type value

The write command tells the defaults command to set a value in the file. The last three arguments specify the symbol, the type of the value, and the value it will be set to. (Refer to the man page for the defaults command for more options.) For example, the drag-and-drop delay used by Xcode's text editor can be adjusted by setting the value for the NSDragAndDropTextDelay key. The command to set this value to 500 (milliseconds) is as follows:

defaults write com.apple.Xcode NSDragAndDropTextDelay -integer 500

To avoid any ambiguity in how the value is interpreted, it's recommended that you include a -type option that casts the value to a specific property list value type. The types that are most useful are:

  • -string (the default)

  • -int or -integer

  • -float

  • -bool or -boolean

When setting Boolean values, the defaults tool is very flexible. It will accept 0, 1, false, true, NO, and YES.

If you ever need to delete a preference value, returning it to its default value, use the delete command, like this:

defaults delete com.apple.Xcode key

The second method is to use the Property List Editor application included with the Xcode development tools. Figure 23-7 shows setting the same value by editing the com.apple.Xcode.plist property list file. Remember not to open the file until after you have quit the Xcode application. The Property List Editor makes setting complex values, like dictionaries and numeric types, much easier. Remember that the com.apple.Xcode.plist file contains all of Xcode's preferences and state settings; be mindful not to alter other internal settings indiscriminately, because it may affect Xcode's stability.

FIGURE 23-7

Figure 23-7. FIGURE 23-7

The following sections list some of the more useful expert preferences settings, grouped by subject. Apple has documented most of these in the Xcode User Defaults document. Search for it in the documentation, or read it online at http://developer.apple.com/mac/library/documentation/DeveloperTools/Reference/XcodeUserDefaultRef/. These hidden settings change from one version of Xcode to the next, so consult this list or the release notes if you can't find what you're looking for here.

Projects and Files

The following table lists the projects and files settings:

SETTING

DEFAULT

DESCRIPTION

PBXCustomTemplateMacroDefinitions

(none)

A dictionary of values used to replace variables in project and file templates. The canonical example is the (now obsolete) __MyCompanyName__ value.

PBXDontWarnIfProjectSaveFails

NO

Setting this to YES suppresses Xcode's warning that there were problems saving the project documents. Project documents are constantly being updated, and Xcode warns you if the file cannot be written. This occurs repeatedly if you have no write access to the document, such as a project on a CD-ROM.

PBXPreservePosixPermissionsOnSave

YES

If set to YES, Xcode tries to restore the POSIX file permissions whenever it saves a file. You may want to change this to NO if you are writing project files to a non-native or networked file system.

NSRecentDocumentsLimit

10

The maximum number of projects to keep in the File

Projects and Files

XCOpenProjectFilesInsideFolders

NO

Setting this to YES lets you open a project document by opening its project folder. In the Open dialog, open a project folder and Xcode will find the first project document and open it instead. Useful if you open a lot of projects that have lots of files in the project folder. This option is known to cause problems with keyboard navigation of the Open dialog.

Editing

The following table lists the editing settings:

SETTING

DEFAULT

DESCRIPTION

NSDragAndDropTextDelay

500

The delay, in milliseconds, that you must hold the mouse button down without moving it before a click in a text selection becomes a text drag. If you are constantly selecting instead of dragging text, reduce this delay. Increase it if you find yourself dragging text when you wanted to select it. Set the delay to 0 (or a negative number) to disable text dragging altogether.

PBXIndentOnPaste

YES

Normally, when you're pasting text into a syntax-aware editor pane, Xcode automatically re-indents the text. If you find this annoying, change this setting to NO and pasted text will be inserted literally. You can always manually re-indent the text using the Format

Editing

PBXBeepOnNoMatchingBrace

YES

Set this to NO to suppress Xcode's habit of playing the system "beep" sound when it can't find a matching brace. The editor looks for matching braces whenever you type a closing brace or parenthesis, or if you double-click a brace or parenthesis.

XCShowUndoPastSaveWarning

YES

Xcode warns you whenever you are about to undo a change that occurred before the file was last saved. If you find this warning annoying, change this setting to NO.

XCColorUnknownLanguages

NO

The Xcode editor normally provides syntax coloring only for languages that it understands. Setting this to YES will cause text that appears to be comments (lines beginning with #, text between /* ... */, and so on), URLs, and string literals to be colored in any text file.

XCCodeSenseAllowAutoCompletionInPlainFiles

NO

Normally, auto-completion is only active in files that Xcode understands. Setting this option to YES enables auto-completion for all file types. Auto-completion will consist of language keywords and text macros.

XCScrollToEndOfMatchingsBrace

YES

When you double-click a quote, brace, or parenthesis, Xcode scrolls so that the matching brace is visible in the editor pane. Set this to NO to leave the scroll position alone.

XCMatchIndentWithLineAbove

YES

When Syntax-Aware Indenting is disabled, or when you're editing a non-syntax-aware source file type, this setting still causes auto-indenting of a new line to the same tab position as the previous line. Setting the value to NO disables auto-indenting of new lines, in the editor's Insert Newline action.

XCSmartInsertDeleteEnabled

NO

Set this to YES and Xcode will try to be "smarter" about how it inserts and deletes spaces around words when inserting and deleting within a source file.

XCCodeSenseFormatting Options

(none)

A dictionary of text macro property values that override those defined by the text macro definition files (.xctxtmacro). See the "Sanctioned Text Macro Customization" section for a description of the values in the dictionary.

XCShowNonBreakingSpace

YES

Xcode normally displays a non-breaking space (Unicode 0x00A0) as a dot (•). Change this setting to NO and non-breaking spaces will display as whitespace.

XCShowControlCharacters

YES

Xcode normally displays control characters that it finds in text files as an inverted question mark (¿). Change this setting to NO and control characters in source files will be invisible.

Functions Menu

The following settings control what kinds of items are included in the Functions menu of the editor's navigation bar. The Show Declarations setting in the Code Sense tab of the Xcode Preferences controls the inclusion of function and method definitions. To include, or exclude, other types of items from the menu change these settings to YES or NO as desired.

SETTING

DEFAULT

PBXMethodPopupIncludeMarksDefault

YES

PBXMethodPopupIncludeClassDeclarationsDefault

YES

PBXMethodPopupIncludeClassDefinitionsDefault

YES

PBXMethodPopupIncludeMethodDeclarationsDefault

YES

PBXMethodPopupIncludeMethodDefinitionsDefault

YES

PBXMethodPopupIncludeFunctionDeclarationsDefault

YES

PBXMethodPopupIncludeFunctionDefinitionsDefault

YES

PBXMethodPopupIncludeTypesDefault

YES

PBXMethodPopupIncludeDefinesDefault

YES

PBXMethodPopupIncludeWarningsDefault

NO

Building

The following table lists the building settings:

SETTING

DEFAULT

DESCRIPTION

BuildSystemCacheSizeInMegabytes

1024

The "trim" size of the precompiled headers cache. Precompiled headers are cached and reused whenever possible. If the size of the cache is larger than this value in megabytes, Xcode deletes the oldest precompiled headers to recover disk space. Note that this happens only once, when the Xcode application is first launched. Setting this value to 0 disables this check, allowing the cache to grow unabated. Also see the BuildSystemCacheMinimumRemovalAgeInHours setting.

BuildSystemCacheMinimumRemovalAgeInHours

24

This is the number of hours a precompiled header must have been in the cache before it can be removed. Even if the BuildSystemCacheSizeInMegabytes setting tells Xcode it's time to delete old headers in the cache, headers that are younger than this setting will never be removed, even if it means not trimming the cache down to the requested size.

PBXBuildSuccessSound

(none)

Set this to the path of a sound file you want played when a build is successful.

PBXBuildFailureSound

(none)

Set this to the path of a sound file you want played whenever a build fails.

PBXNumberOfParallelBuildSubtasks

(none)

The number of parallel tasks the build system will try to keep running while building. If not set, the build system uses the number of processors installed in your computer. Set this to a number greater than the number of processors if your builds are I/O bound. Reduce the number to keep Xcode from using all available CPU resources.

PredictiveCompilationDelay

30

The number of seconds before a predictive compile is performed. If a source file in an editor pane hasn't been modified for a while, Xcode attempts to compile it in the background. Increase this delay if background compilation is using too many resources, or reduce it to be more aggressive. This feature requires that predictive compilation is enabled in the Xcode Preferences. Xcode ignores this setting if you try to set it to 10 or less.

UsePerConfigurationBuildLocations

YES

Build locations are normally separated into subfolders by build configuration. This avoids the need to rebuild the entire project when switching build configurations, but uses considerably more disk space. Set this to NO, and the build products of different build configurations will be written to the same folder.

Distributed Builds

The following table lists the distributed builds settings:

SETTING

DEFAULT

DESCRIPTION

XCMaxNumberOfDistributedTasks

25

The maximum number of tasks to distribute to other computers when you're using distributed builds.

XCDistributedBuildsVerboseLogging

NO

Change this setting to YES to enable diagnostic messages from the distcc tool. If you are having problems with distributed builds, these messages may provide some insight as to why.

DistributedBuildsLogLevel

0

Controls the amount of detail produced by Xcode's distributed build manager. This is useful for debugging distributed build problems. The value must be 0, 1, or 2.

Debugging

The following table lists the debugging settings:

SETTING

DEFAULT

DESCRIPTION

XCAutoClearRunDebugStdIOLogs

NO

Set this to YES and Xcode will clear the run, debug, and standard I/O windows at the beginning of each debugging or run session. Normally, Xcode preserves the results of the previous run or debug session, allowing the output of those to accumulate until you quit Xcode or manually clear the log windows with the Debug

Debugging

PBXGDBPath

/Developer/usr/bin/gdb

The path to the gdb debugger. Change this setting to use an alternate version of the gdb debugger. Note that for remote debugging, Xcode's default is /usr/bin/ssh.

PBXGDBDebuggerLogToFile

NO

If you think you need to debug the communications between Xcode and the debugger, change this setting to YES. This causes Xcode to log all communications between Xcode and the gdb tool to a file in /var/tmp/folders.<uid>/Temporary Items. The name of the file is determined by the PBXGDBDebuggerLogFileName setting. If you are having problems with the debugger, Apple requests that you include this log file in any bug reports.

PBXGDBDebuggerLogFileName

(none)

If left undefined, the name of the debugger log file will be XCGDB-name-pid, where name is the name of the executable and pid is its process ID. Setting this to a fixed value causes the log to be written to the same file for every debug session, overwriting any previous file. This setting requires that PBXGDBDebuggerLogToFile is set to YES to have any effect.

Snapshots

This single setting determines where snapshots are stored. If you have a scratch drive you may want to direct the snapshot repository to there. If you've previously taken snapshots, remember to relocate the SnapshotRepository.sparseimage file to its new location before launching Xcode, or else Xcode will forget all of your snapshots.

SETTING

DEFAULT

DESCRIPTION

XCSnapshotDiskImagePath

~/Library/Application Support/Developer/Shared/SnapshotRepository.sparseimage

Path to the sparse disk image document that Xcode uses to store snapshots.

Source Code Management

The path setting specifies the path to the Perforce source control client tool. The CVS and Subversion client tools are fixed.

SETTING

DEFAULT

DESCRIPTION

PBXPerforceToolPath

/usr/local/bin/p4

The default path to the Perforce client tool.

XCSMLogSize

500

The maximum amount of text (in K) that will be kept in the SCM log, and that can be viewed in the SCM Results window.

Documentation

The following table lists the documentation setting:

SETTING

DEFAULT

DESCRIPTION

XCDocWindowSharesGlobalFindString

YES

When this is set to YES, the search field for the help window automatically picks up the value of the global find string. This is a system-wide resource shared by Xcode's find windows and other find-savvy applications. If you make a search in Mail, for instance, switching to Xcode automatically picks up the last term you searched for. Change this setting to NO to suppress this behavior.

TEMPLATES

Although this is not officially documented, it's also possible to customize Xcode by adding your own project and file templates. Templates are installed in the File Templates and Project Templates folders found in the /Developer/Library/Xcode folder. You can customize the existing one or add your own here. Like text macros, Xcode no longer searches the system (/Library) or user (~/Library) domains for templates, so if you want to customize them you'll have to hack the set that comes installed with Xcode.

The hierarchy and names of the subfolders within the templates folder determine the grouping and order of the templates that will appear in the New File or Project assistant. The easiest way to see this is to compare the file structure of a template folder with the new file assistant, shown in Figure 23-8.

FIGURE 23-8

Figure 23-8. FIGURE 23-8

You can group your templates however you want, simply by placing them into a subfolder of related templates, with one exception: some groups have a .plist file that defines additional information about the group, selection options, and so forth. If a folder contains a .plist file you will have to edit it to include your template definition or else Xcode will ignore it. If the folder doesn't have a .plist file, just drop in your template and it will appear in Xcode.

File Templates

File templates can be a single file or a file template bundle. The simplest way to add a file template is to place a plain document file in the File Templates folder. The next time you start Xcode and use the File

File Templates

File template bundles are a little more sophisticated. File template bundles are not real bundles, but are folders that mimic the bundle structure. A file template bundle has an extension of .pbfiletemplate. The name of the folder is the name of the template, as it will appear in the New File assistant. Inside the folder is a TemplateInfo.plist file and one or two document files. TemplateInfo.plist contains a number of properties, described in the following table:

PROPERTY

DESCRIPTION

MainTemplateFile

This property is required. It is the name of the primary template file in the bundle.

CounterpartTemplateFile

This property is the name of a companion file that can be created by the template. This property is optional. If present, Xcode displays a check box option in the new file dialog box that asks if the user wants to create the companion file at the same time they create the main file.

Description

Text that describes the template. This description appears in the lower pane of the new file assistant when the user selects this template in the list. This property is optional, but highly recommended. If omitted, Xcode displays "No description available."

Create the template file or template bundle, name it appropriately, and place it in the File Templates folder or in a subfolder if you want it to be in a group of templates. Relaunch Xcode and your new template appears in the new file assistant.

Template Macros

Templates can contain variable names that are replaced with a value when the template is read. The macro names are surrounded by double-angle quotes (←NAME→). The following table lists the macro variables defined by Xcode when a file template is read.

Warning

The double-angle quote characters, which are Unicode characters 0x00AB and 0x00BB, respectively, require that the file be encoded correctly — "Correctly" being defined as whatever Xcode expects the encoding to be. Open the template file in Xcode. If the double-angle quotes appear as one or two strange characters, then the encoding is mismatched. First note the current encoding. Now, try switching to a different encoding using the View

Template Macros

MACRO

EXAMPLE

DESCRIPTION

DATE

11/17/05

Today's date, short format.

YEAR

2006

The year.

FILENAME

My File.txt

The complete name of the new file.

FILEBASENAME

My File

The filename given to the file by the user, without its extension.

FILEBASENAMEASIDENTIFIER

My_File

The base filename, suitable for use as a language identifier. The same as FILEBASENAME, but with all non-alphanumeric characters replaced with underscores.

FULLUSERNAME

James Bucanek

The current user's full account name.

PROJECTNAME

Tom & Jerry

The name of the project.

PROJECTNAMEASIDENTIFIER

Tom___Jerry

The name of the project, suitable for use as a language identifier. The same as PROJECTNAME, but with all non-alphanumeric characters replaced with underscores.

PROJECTNAMEASXML

Tom &amp; Jerry

The name of the project encoded using XML entities to escape any special characters.

USERNAME

james

The current user's UNIX account name.

UUID

89E2FBF6-9B88-40EB-BFCF-4550CA9F54CA

A Universally Unique Identifier. This value will be different every time.

ORGANIZATIONNAME

Genius, Inc.

A common macro defined in the expert preferences.

The UUID value is interesting and might be useful if the documents you are creating need to be managed by a database or identified in some fashion.

The ORGANIZATIONNAME macro was described in the "Who's __MyCompanyName__?" section of Chapter 4.

The PROJECTNAME and related macros are defined for project templates or if the file is being added to a project. If you create a new file using a template but select "none" as the project to add it to, these variables are replaced with nothing. In fact, any undefined or unrecognized macro name is replaced with nothing in the new file.

Project Templates

You can also create your own project templates. If you want to create a simple project template that gets duplicated verbatim when created, follow these steps:

  1. Create a new project. Configure the project the way you want it: Add source files, frameworks, targets, special settings, and so on.

  2. Close the project. Delete the build folder and any other intermediate files that might be in the project folder.

  3. Rename the project folder to the name of the template as you want it to appear in the new project assistant.

  4. Move the project folder to a location in the /Developer/Library/Xcode/Project Templates folder where you want it to appear in the new project assistant list. Like file templates, some of these groups have .plist files that define the templates for that group. Copy your template to a folder without a .plist file, such as the Other folder, or edit the .plist file to include the new template.

  5. Quit Xcode. Relaunch Xcode and create a new project using your template.

Project Templates with Macros

Simple project templates are easy, but boring. What you really want are project templates like those that ship with Xcode. These include files, class names, and project settings that magically alter themselves to match the name of the project you just created.

Making a project template that will customize itself is considerably trickier than what's involved in making a file template. How much of your project gets dynamically configured depends on how much work you want to put into it. The key to configuring a self-customizing project template is to create a TemplateInfo.plist file and embed that in your project document package. The TemplateInfo.plist file should contain three properties: Description, FilesToRename, and FilesToMacroExpand as described in the following table:

PROPERTY

TYPE

DESCRIPTION

Description

String

This optional property is a string that describes the template. This description appears in the lower pane of the new project assistant window when a user selects this template from the list.

FilesToRename

Dictionary

This optional property is a list of key/value pairs. Each pair consists of the name of the original file in the project and the name it should be renamed to when the new project is created.

FilesToMacroExpand

Array

This optional property is a list of file name paths, relative to the new project folder, of the files that should be scanned for replicable template macro names.

You should definitely supply a description string. It makes the template more pleasant to use, and is useful for debugging (explained later).

The FilesToRename property is a translation table that renames certain files in the project as the project template is being duplicated. The values in this dictionary can include template macros, which allow you to give files in your project dynamic names. In the example shown in Figure 23-9, the Template_Prefix.pch file in the project template will be renamed to ←PROJECTNAME→_Prefix.pch. PROJECTNAME will be replaced with whatever filename was given to the new project by the user.

You can use any of the template macros listed previously in the "Template Macros" section in the project document or in the source files of your project template. The files in the project document package are automatically scanned for template macro names. This is how the macro names in the TemplateInfo.plist file are expanded. Also scanned is the project.pbxproj document. Thus, any build settings in the project that contains a template macro name will be replaced. This allows you, for example, to set the Prefix Header build setting to ←PROJECTNAME→_Prefix.pch so it will match the name of the renamed Template_Prefix.pch file in the new project.

FIGURE 23-9

Figure 23-9. FIGURE 23-9

Other files in the project are not automatically scanned for template macro names. To replace template macro names in any other files requires that you add its path to the FilesToMacroExpand property. The names in the list are the files in the new project, not t template, so if you want to process a file that you've also renamed, use the name the file was changed to — which itself will probably involve template macros.

In the example previously shown in Figure 23-9, the ←PROJECTNAMEASIDENTIFIER→Helper.h files are scanned for template macros. The original file in the TemplateHelper.h template looks like this:

TemplateHelper.h

//
//  ←PROJECTNAMEASIDENTIFIER→Helper.h
//  ←PROJECTNAME→
//
//  Created by ←FULLUSERNAME→ on ←DATE→.
//  Copyright ←YEAR→ ←ORGANIZATIONNAME→. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface ←PROJECTNAMEASIDENTIFIER→Helper : NSObject
{

}

@end

Not only will the file be renamed to match the project, but the class it defines will also get a matching name.

File References in Project Templates

The FilesToRename property renames files in the project folder. The macro replacement can be used to generate names dynamically in files and project properties. Unfortunately, these two mechanisms don't work closely with each other or with the project itself. The FilesToRename property just renames files. It doesn't alter or fix up any of the project references to those files. If nothing else is done, the project will contain bad references to the original files. To fix this, you must manually insert template macros into the project.pbxproj file. You can do this in Xcode or the Property List Editor by temporarily giving the project.pbxproj file an extension of .xml or .plist, or you can use another text editor like BBEdit.

Warning

If you edit template property files with BBEdit, make sure you use the correct encoding. Property list files typically have an encoding of UTF-8. If you open one of these files using ASCII encoding, the double-angle quote characters will not be encoded correctly. Use BBEdit's File

File References in Project Templates

You'll have to find and replace the filename paths in the property files by hand, because there is no facility for entering these dynamic names in the Xcode interface. The format for the project document file is not intended to be "user friendly." Nevertheless, it's pretty safe to search for the file names you want to make dynamic and replace them with template macros. Just be careful not to make any other structural changes in the file, or you'll likely end up with a corrupted project document.

For example, here's a fragment of a project.pbxproj file that contained references to the TemplateHelper.h, TemplateHelper.m, and main.c source files:

1AAE3664092E3E8c23412C87 /* TemplateHelper.h */ = {isa = PBXFileReference;
fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path =
←PROJECTNAMEASIDENTIFIER→Helper.h; sourceTree = "<group>"; };

1AAE3665092E3E8c23412C87 /* TemplateHelper.m */ = {isa = PBXFileReference;
fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path =
←PROJECTNAMEASIDENTIFIER→Helper.m; sourceTree = "<group>"; };

29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference;
fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m;
sourceTree = "<group>"; };

The file has been edited so that the first two source file names are now altered dynamically to match their renamed versions in the new project.

Problems with Project Templates

Template project problems can be difficult to isolate, because there are no overt errors or warnings produced by Xcode to tell you that something is wrong.

The first thing to check in your new template is that its description appears in the new project assistant window when you choose your template in the list. If it does not, then Xcode didn't read your TemplateInfo.plist file. Make sure the location, syntax, and encoding of the file is correct. You might find that the easiest way of doing this is to open the file using the Property List Editor and forcing the file to be resaved. The Property List Editor usually corrects any inconsistencies when it writes a new file.

If you have macros in source files that aren't being expanded, make sure they have been written using the default encoding expected by Xcode for that file type. Follow the steps for fixing the encoding in the earlier "Template Macros" section. Also double-check that you've added the file to the FilesToMacroExpand property. If the file is one that gets renamed, make sure you specified its new name — not its original name — in the template.

Look at the system console log. Some problems encountered during template processing are logged here and may give you some clue as to what is wrong.

Last, but not least, study (or just copy) the Xcode templates that come preinstalled. They demonstrate a wide range of customizations. Learning how they work may illuminate what's not working in yours.

Project Template Portability

If you are creating project templates for your own consumption, you're pretty much done. However, if you want to create sophisticated templates to share with other developers, there are a couple of additional details you should consider.

You'll want to delete your user settings files from the project document package. These documents are stored inside the project document package and are named using your logged-in UNIX account name. Other users don't need these documents in their projects.

Target Templates

You may also find it useful to create custom target templates. These are the templates used by the new target assistant when you're adding a new target to your project. Target templates are defined by the target template files found in the /Developer/Library/Xcode/Target Templates folder. A target template file is a property list fragment with an extension of .trgttmpl. Several properties must be set correctly for the target template to be functional. The important elements are the Class and ProductType properties. The easiest way to create a new target template is to copy a template file that creates the correct target type and edit its other properties. This listing shows the target template file for a Cocoa application target:

{
    Class = Native;
    ProductType = "com.apple.product-type.application";
    Description = "Target for building an application that uses Cocoa APIs.";
    CustomBuildSettings = {
        INSTALL_PATH = "$(USER_APPS_DIR)";
        INFOPLIST_FILE = "←PRODUCTNAME→-Info.plist";
        OTHER_LDFLAGS = "-framework Foundation -framework AppKit";
        GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/
AppKit.framework/Headers/AppKit.h";
        GCC_PRECOMPILE_PREFIX_HEADER = YES;
        PRODUCT_NAME = "←PRODUCTNAME→";
        PREBINDING = NO;
        GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
        GCC_MODEL_TUNING = G5;
    };
    CustomProductSettings = {
        CFBundleExecutable = "←PRODUCTNAME→";
        CFBundleInfoDictionaryVersion = "6.0";
        CFBundleVersion = "1.0";
        CFBundleIdentifier = "com.yourcompany.←TARGETNAMEASIDENTIFIER→";
        CFBundleDevelopmentRegion = English;
        CFBundlePackageType = "APPL";
        CFBundleSignature = "????";
        NSMainNibFile = "MainMenu";
        NSPrincipalClass = "NSApplication";
    };
    BuildPhases = (
        {
            Class = Resources;
        },
        {
            Class = Sources;
        },
        {
            Class = Frameworks;
        },
    );
}

After you make a copy of the template file, edit the Description, CustomBuildSettings, CustomProductSettings, and BuildPhases properties. The CustomBuildSettings can define any build settings you want and can refer to template macro values. The PRODUCTNAME and TARGETNAMEASIDENTIFIER template macros are defined while creating a new target and can be used to refer to the new target's name.

The CustomProductSettings are present for targets that produce an Info.plist file and contain a list of customized values that will appear in the Properties pane of the target's Info window.

The BuildPhases property lists the build phases for the new target. The possible BuildPhase types are

  • Aggregate

  • Application

  • Bundle

  • CopyFiles

  • Frameworks

  • Headers

  • JavaArchive

  • Legacy

  • Library

  • Native

  • Resources

  • ShellScript

  • Sources

  • Tool

Refer to other templates, or first create a target of the desired type and add your desired phases to it, to ensure that the target can accept a particular build phase type. You should not include a build phase in a target type that does not normally accept that build phase type.

Name your target template file and place it where you want it to reside inside the Target Templates folder. Relaunch Xcode to use the new template.

USER SCRIPTS

User scripts are custom actions that appear in the Xcode menu. Each script is an executable text file that can optionally interact with the content of your active editor pane, essentially allowing you to extend the editor with your own commands. You can write your scripts using a shell language, perl, python, ruby, awk, or any other interpreter you want.

Warning

Although the script is in the form of an executable file, you cannot substitute a binary executable. The file must be a text file encoded using UTF-8. However, there is nothing stopping a script from launching another binary executable or script. For example, a custom script could start an AppleScript using the osascript tool.

Xcode preprocesses script files before they are executed. Scripts contain additional properties and commands that enable them to communicate with the Xcode application — in a fashion. Because of the subtle differences between regular executable scripts and custom Xcode scripts, the following sections use the term "custom script" to indicate an executable script that employs special Xcode syntax.

The StartupScript

When Xcode starts, it looks for the custom script /Developer/Library/Xcode/StartupScript and executes it. This script is a custom script that can employ any of the special custom script extensions explained later — at least those that make sense in the absence of an editor context. You can modify this script to automatically perform any action you want every time Xcode starts.

The StartupScript is worth mentioning, from an historical perspective, because this is where you used to install custom scripts in the Xcode menus. That ability has been formalized, as you'll see in the next section, so the StartupScript is no longer the appropriate place for customizing Xcode's interface. If your StartupScript creates menu items or keyboard shortcuts, now would be a good time to remove those and add them back using the Edit User Scripts interface.

Creating a User Script

Choose the Edit User Scripts command from the script menu — that's the menu that looks like a scroll. This command opens the Edit User Scripts window, shown in Figure 23-10.

FIGURE 23-10

Figure 23-10. FIGURE 23-10

The Edit User Scripts window is almost identical to the actions window used to define custom organizer actions. The only significant differences are that the Add menu includes a New Submenu command and user scripts have different options than action scripts. This is where you create, add, name, arrange, and assign keyboard shortcuts to user scripts. See Chapter 22 if any of this seems unfamiliar. The user scripts window maintains a hierarchy of submenus, which you can edit and reorganize.

User Script Options

User scripts can be invoked while editing and are intended to augment the capabilities of the Xcode editors. User scripts can do anything, but they typically process the file or text selection by digesting the text in the editor pane and replacing it with its output. The user script's options determine how the script interacts with the document. Some of these options duplicate menu script variables, described later.

A user script has four options:

  • Input

  • Directory

  • Output

  • Errors

The Input option determines what information will be piped to the script on its stdin. The choices are None, Selection, and Entire File. Unlike the organizer, Selection in this case means exactly what it says: the span of text currently selected in the editor. Use this when you want your script to filter or process the current selection. The Entire File choice pipes the entire contents of the current editor pane to the script, and of the current editor pane, and None doesn't supply anything at all.

The Directory option sets the working directory to one of three locations: Selection, Home Directory, or File System Root. These choices will set the working directory to the one that contains the file in the editor pane (.), your user's home directory (∼), or the file system's root directory (/), respectively. This is usually only useful for scripts that need to process other files, and the setting isn't normally significant.

The Output and Errors settings determine what happens to any text output by the script. The following table lists the Output settings and what will happen with the text the script sends to stdout:

OUTPUT

DESCRIPTION

Discard

The output of the script is discarded. This is the default.

Replace Selection

The output of the script replaces the current selection in the editor pane.

Replace Document Contents

The output of the script replaces the contents of the entire editor pane.

Insert After Selection

The output of the script is inserted into the active file immediately following the current selection or insertion point.

Insert After Document Contents

Appends the output of the script to the end of the editor pane.

Open in New Window

Opens a new editor window and writes the output of the text to it. The original source file is not affected.

Open as HTML

Same as Open in New Window, but the output is interpreted and displayed as a web page.

Place on Clipboard

Transfers the output of the script to the clipboard. The original source file is not affected.

Display in Alert

Displays the output of the script in an alert dialog.

Similarly, the Errors setting determines the disposition of text output to stderr. The choices are Ignore Errors, Display in Alert, Place on Clipboard, and Merge with Script Output.

Anatomy of a User Script

A custom script is, above all else, an executable script file. The first line of the file must be a "shebang" line that informs the system of the application that will be used to interpret the script. The first line of a bash script would be as follows:

#! /bin/bash

The file must be encoded using UTF-8 or an encoding that is compatible with UTF-8. UTF-8 is a superset of the plain ASCII encoding, so any interpreter that requires ASCII source will be compatible.

A custom menu script can contain special tokens that enable it to interact, in a fashion, with the Xcode application. These consist of menu script definitions and custom script variables. Custom script tokens are surrounded by the character sequences %%%{ and }%%%. An example is the expression %%%{PBXFilePath]%%%. This user script variable will be replaced with the path of the file being edited when the script is preprocessed. You can think of user script variables as shell macros, except that they are substituted prior to the script's execution. From the script's perspective, they appear as a literal value.

Note

Prior to the introduction of the Edit User Scripts interface, user scripts were added to the menu by the StartupScript using special user script declarations that defined the script's name in the menu, its keyboard shortcut, its input and output options, and so on. If you're incorporating an older user script, remove declarations like PBXName=, PBXKeyEquivalent=, PBXInput=, and PBXOutput=. Use the values from those declarations to set equivalent options when configuring the script in the Edit User Scripts window.

Scripts can also call a number of utilities provided by the Xcode tools framework. These are executable programs and scripts that can be called by your custom script. See the "Script Helpers" section for the script utilities you can use.

User Script Variables

User script variables are replaced by the value obtained from the currently active editor pane. Again, this substitution happens prior to the beginning of script execution, so treat these variables as constants in your script. Here's an example that uses the PBXSelectionLength variable that contains the number of characters in the user's current text selection when the script is executed:

if [ %%%{PBXSelectionLength}%%% == 0 ]; then echo "No Selection"; exit; fi

If the value of PBXSelectionLength is 8, the actual line of text in the script that is executed by the interpreter will be:

if [ 8 == 0 ]; then echo "No Selection"; exit; fi

PBXFilePath

The PBXFilePath variables expand to the complete pathname of the file in the editor pane. You can use this instead of, or in addition to, piping the contents of the document to the script's input. It's also useful for writing scripts that perform some action on the file itself — like a source control script — instead of the contents of the editor pane.

Warning

This variable works reliably when used in an editor pane displaying an existing file. In other situations — like a text selection in the build transcript or a text file window that's never been saved — its behavior is erratic. It may evaluate to nothing, a path that isn't a file, or a path to a temporary file. Code defensively when using this value.

PBXSelectedText and PBXAllText

The PBXSelectedText and PBXAllText variables expand to the contents of the current text selection or the contents of the entire editor pane, respectively. You can use these variables instead of, or in addition to, the Input option of the script.

These can be rather dangerous to use in a script. They are replaced, verbatim, with the contents of the selection or editor pane. There is no protection from special characters that might be inappropriate at that location in your script. In other words, the substitution may result in portions of the editor text being interpreted as part of the script. For example, the following shell statement appears harmless, but it will cause the script to fail with a syntax error if the currently selected text contains a double quote character:

SELECTION="%%%{PBXSelectedText}%%%"

One way to avoid this kind of problem in the shell is to use a so-called "here document,"like this:

cat << END_OF_SELECTION
%%%{PBXSelectedText}%%%
END_OF_SELECTION

Most shells and interpreters like perl support some kind of "here document" syntax. It's more robust than trying to quote the value, but still isn't foolproof. Consider an editor pane where the text "END_OF_SELECTION" is the current selection.

PBXTextLength, PBXSelectionStart, PBXSelectionEnd, and PBXSelectionLength

These four variables, described in the following table, report the number of characters in the file or current text selection and the index into the current document where the text selection begins and ends.

VARIABLE

DESCRIPTION

PBXTextLength

The number of characters in the active editor pane.

PBXSelectionLength

The number of characters in the current text selection. This will be 0 if the text selection is an insertion point.

PBXSelectionStart

The position within the editor pane where the current text selection or insertion point begins.

PBXSelectionEnd

The position within the editor pane where the current text selection ends.

Using these variables, your user script can treat the contents of the editor pane as a whole. A common scenario is to set the script's Input option to Entire Document and its Output option to Replace Document Contents. The script reads the entire document into a variable and then uses these four user script values to identify the selected text. The script has the entire contents of the document, knows the location of the current selection, and can change anything within the document.

These variables are typically more useful when used with interpreters, like perl and ruby, that provide more sophisticated string and character functions.

PBXSelection

The PBXSelection variable is replaced by a special marker — some obscure sequence of characters known only to Xcode. The Xcode editor looks for this special marker, or markers, in the text output by the user script. If it finds these markers in the text, it uses them to set the text selection in the editor pane. This only applies when the output of the script is being used to replace or insert text in the editor pane.

Including one PBXSelection marker in your script's output causes the insertion point to be placed at that position. Including two PBXSelection markers causes everything between the two to be selected. This listing shows a custom script that inserts a HeaderDoc comment and leaves the class name selected:

#! /bin/bash

cat << END_OF_HEADERDOC
/*!
    @class      %%%{PBXSelection}%%%Class%%%{PBXSelection}%%%
    @abstract
    @discussion
    */

END_OF_HEADERDOC

The two selection markers in the output text are caught by Xcode and used to establish a new text selection in the editor pane, as shown in Figure 23-11.

FIGURE 23-11

Figure 23-11. FIGURE 23-11

Script Helpers

Xcode — or more precisely the Xcode developer tools framework — provide a number of utility programs that can be called by a custom script to programmatically interact with Xcode and the user.

These tools are in a framework bundle added by the Xcode Developer Tools installer. Use the path supplied in the PBXUtilityScriptsPath custom script variable to locate the tools. The following script demonstrates using the PBXUtilityScriptsPath variable to execute the AskUserForExistingFileDialog tool:

TOOLSPATH='%%%{PBXUtilityScriptsPath}%%%'
"${TOOLSPATH}"/AskUserForExistingFileDialog "Choose a text file"

Prompt for a String

The AskUserForStringDialog tool prompts users for some text, which they can enter interactively via a dialog box. The single, optional, argument specifies the default text value that will appear in the dialog box when it is opened. The text entered by the user is returned via stdout. The following example bash script prompts for a username, supplying the current account's short name as a default, and captures the results in the variable NEWNAME:

NEWNAME="$('%%%{PBXUtilityScriptsPath}%%%/AskUserForStringDialog' ${USER})"

Ask for an Existing File or Folder

AskUserForExistingFileDialog and AskUserForExistingFolderDialog prompt the user to select an existing file or folder, respectively. Each takes a single, optional, prompt argument that will be visible in the dialog box. The path to the selected file or folder is returned via stdout. If the user clicks the Cancel button in the dialog, the return value is empty. You have no control over the type of file the user can select. The dialog displays, and allows the user to choose, invisible files and folders.

Prompt for a New File

The AskUserForNewFileDialog tool prompts the user to choose a filename and location for a new file. The path to the new file is returned via stdout. The tool takes two, optional, arguments. The first is a prompt string that will be visible in the dialog box. The second is a default filename for the new file. To specify a default filename but no prompt, pass an empty prompt like this:

'%%%{PBXUtilityScriptsPath}%%%/AskUserForNewFileDialog' "" "New.txt"

Ask for an Application

AskUserForApplicationDialog presents the user with an application picker dialog. This dialog, shown in Figure 23-12, enables the user to choose an application known to launch services, or browse the file system in search of an unknown one. The tool returns the full path to the application's program file or bundle folder, as appropriate.

FIGURE 23-12

Figure 23-12. FIGURE 23-12

The command takes two, optional, arguments. The first is the title used for the dialog, normally "Choose Application." The second argument is a prompt string, normally "Select an application."

APPLESCRIPT

In addition to the many ways in which Xcode can run automated scripts, the Xcode application itself can be driven programmatically using AppleScript. Open the AppleScript dictionary for Xcode, shown in Figure 23-13, and you will find a rich and complex set of objects and command to work with.

FIGURE 23-13

Figure 23-13. FIGURE 23-13

Closely related to AppleScript are Automator actions. The Xcode Developer Tools includes several Automator actions, shown in Figure 23-14, that allow Xcode processes to be integrated into Automator workflows.

FIGURE 23-14

Figure 23-14. FIGURE 23-14

Although you can build a project using the xcodebuild tool, that's about all you can do with it. AppleScript provides the ability to automate Xcode by accessing the data and structures within projects and the Xcode application itself. For example, your company may have a set of target settings that need to be uniform across multiple projects. You can write an AppleScript program to quickly check, and possibly correct, those properties in dozens of projects containing potentially hundreds of individual settings. Or maybe you just like your windows stacked up in certain way. The possibilities are almost endless.

Remember too that AppleScripts are supported directly by the organizer (see Chapter 22). Once you write and save your AppleScript, you can attach it to a project in the organizer as an action.

AppleScript programming is beyond the scope of this book, but here are a few tips for using AppleScript with Xcode:

  • A shell script can start an AppleScript using the osascript tool. This enables you to utilize AppleScript in build phases and custom commands. You can also use AppleScript to integrate applications that couldn't normally be controlled by a build script, such as an audio or image conversion program.

  • The AppleScript Standard Additions allow an AppleScript program to run a shell script, meaning you can freely mix AppleScript and shell scripting technologies.

  • Although Xcode lets you create AppleScript Studio applications, debugging an AppleScript application that is trying to interact with Xcode at the same time can be problematic. If you can't debug your script because the script is trying to use Xcode at the same time, switch to another AppleScript editor or debugger like the AppleScript Editor application included with Mac OS X.

RESETTING YOUR XCODE CUSTOMIZATIONS

If you ever want to wipe all of your customizations and put Xcode back the way it came when you first installed it, then close the Xcode application, open up a Terminal window, and issue the following two commands:

defaults delete com.apple.Xcode
rm -rf ~/Library/Application Support/Xcode

This will reset all per-user customizations and restore Xcode to its factory defaults for the logged-in user.

SUMMARY

In earlier chapters, you learned to customize your project and how it is built. In this chapter, you learned to customize your Xcode environment beyond the many options already exposed in the Xcode Preferences window. You can set invisible features, add your own processing scripts to any editor pane, and develop your own file, project, and target templates to make repetitive tasks in Xcode easier and more productive.

This brings us to the end. I sincerely hope that this book has provided you with a well-rounded introduction to Xcode, a clear explanation of its core concepts, and an appreciation of those facilities and features that you might not have known existed. My only remaining desire is that you take what you've learned here and use it to create award-winning, bug-free, software for one of my favorite computer platforms.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset