Xcode

From HerzbubeWiki
Jump to: navigation, search

This page contains my notes about Xcode, the IDE used for writing applications for Mac OS X and iOS devices (iPhone, iPad). For the moment, there is also a section about Interface Builder (IB), the RAD tool to develop the GUI of Mac applications.


Developer Tools

Since version 4.3, Xcode is distributed as a standalone app bundle. A number of additional developer tools that Apple now considers optional must be installed separately. Things I habitually install are

  • Command line tools: These are required for software builds outside of Xcode, such as Fink, or builds of third party software packages that I use for my own projects. The easiest way to install these is from within Xcode: Open the Preferences dialog, then go to Downloads > Components, select the entry "Command Line Tools" and click the install button.
  • iOS simulator applications for older versions of iOS: These can also be installed from within Xcode in the same way as the command line tools.
  • Graphics Tools for Xcode: One of the most useful applications in this tool suite is Pixie, which allows to inspect displayed graphics on a pixel level with a "magnifying glass" effect. From within Xcode, select the menu entry "Xcode > Open Developer Tools > More Developer Tools...". This opens a browser window that shows all additional Xcode tool packages. The Graphics Tools package should be among these.
  • Audio Tools for Xcode: See "Graphics Tools" for how to get at these tools.
  • Auxiliary Tools for Xcode: See "Graphics Tools" for how to get at these tools.


Xcode plugins

Xcode plugins use the extension xcplugin. Installing a plugin is very simple, just drop it into

~/Library/Application Support/Developer/Shared/Xcode/Plug-ins

Create the directory if it does not exist yet. To install a plugin system-wide, on Mac OS X 10.6 it needs to be dropped into

/Developer/Library/Xcode/Plug-ins

Useful plugins

  • GTMXcodePlugin from the Google Toolbox for Mac: Provides an option to trim trailing whitespace when a file is saved. Note that this plugin is no longer necessary as newer versions of Xcode now have an official user preference for this.


Xcode Preferences

Preferences dialog

These settings refer to the Preferences dialog as shown in Xcode 4.5.

Behaviors

  • Running completes
    • Hide debugger = true (default = if no output, hide)

Text Editing > Editing

  • Line numbers = true
  • Show column position = true
  • Page guide = true, at column = 80
  • Automatically trim trailing whitespace = true
  • Including whitespace-only lines = true
  • Default text encoding = UTF-8
  • Default line endings = OS X / Unix (LF)
  • Convert existing files on save = false

Text Editing > Indentation

  • Prefer indent using = Spaces
  • Tab width = 2
  • Indent with = 2
  • Tab key = Indents in leading whitespace
  • Line wrapping = false
  • Align consecutive // comments = true

Locations

  • See section Xcode paths
  • The "Archives" location should be changed to something like /Users/foo/dev/Xcode Archives so that the archived builds are indexed by Spotlight. Without Spotlight indexing, symbolicating crash logs will probably fail. For details about symbolicating see the wiki page on Mac OS X Programming.

Snapshots

  • File > Project Settings > Snapshots > Create snapshot of project before mass-editing operations = false
    • I don't see the use for snapshots when Ialso use version control
    • In the past this automatic-snapshot feature made it virtually impossible for me to use refactoring because the snapshot would include the entire project directory with all third party software builds inside (hundreds of MB)


Hidden preferences

  • Get rid of the warning message box when you undo after saving a document (note: this is probably no longer necessary for Xcode 4.5):
defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO


Keyboard shortcuts

Useful shortcuts

These shortcuts are defaults in Xcode 4.5:

  • Close document = Ctrl + Command + W
  • Jump to next/previous counterpart = Ctrl + Command + Cursor Up/Down
  • Open quickly = Command + Shift + O
    • In the window that opens just start to type to incrementally filter possible documents to open
    • The word under the cursor is used for initial suggestions
  • Commit dialog = Alt + Command + C
  • Use selection for Find = Command + E
    • Unlike a similar function in Visual Studio (Ctrl+Tab) this does not immediately perform a search
    • To follow up with a "search next", press Command + G


Changes to defaults

TODO


Xcode paths

  • Build results
    • By default these are stored in
/Users/foo/Library/Developer/Xcode/DerivedData
    • This folder can be changed to a project-relative path under "Preferences > Locations > Locations > Derived Data"
  • Archives and Snapshots
    • By default these are stored in
/Users/patrick/Library/Developer/Xcode/Snapshots
/Users/patrick/Library/Developer/Xcode/Archives
    • These folders can be changed under "Preferences > Locations > Locations"
  • Symbols downloaded from an iOS device
/Developer/Platforms/iPhoneOS.platform/DeviceSupport/<firmware_version>/Symbols


Workspaces, projects, build configurations, targets, schemes

References


Summary

  • A workspace is a container for one or more projects
  • A project contains
    • One or more build configurations
    • One or more targets
    • One or more schemes
    • The actual source code and resource files (obviously)
  • A build configuration is just a name. Typically there are two build configurations: Debug and Release.
  • A target is a collection of the following things:
    • For each configuration, a collection of build settings
    • For each configuration, a definition of which source code files need to be built
    • For each configuration, a definition of additional files that belong to the target/configuration, usually resource files
  • A scheme is a collection of the following things:
    • It contains one or more targets
    • It defines what happens for each of its targets when one of the actions in the "Product" menu is executed: Build, Run, Test, Profile, Analyze, Archive
    • The definition for each action includes which build configuration to use


Additional notes:

  • A target is inactive until it is associated with at least one scheme


Workspace

A workspace's data is stored in a folder that has the extension

.xcworkspace

The Finder treats the folder as a bundle, i.e. for the user it appears a file that can be double clicked.

Example workspace content

caradhras:~/dev/littlego --> cat "Little Go.xcworkspace/contents.xcworkspacedata"
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Little Go.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>


Non-admin account requires group membership

If you are logged in as a normal (non-admin) user, when you select "Build and debug" for the first time in Xcode you will be asked for the admin password. This happens once every login session. I am on Snow Leopard and use Xcode 3.2.5.

This is rather unexpected, but the Xcode release notes indeed mention that this is the case in the current seed of the developer tools, but that "the security mechanisms and policy here are still under development for Snow Leopard, and may change further in later seed releases.". In the same release notes, but on another note, the following explanation appears: "All Xcode operations that operate between processes now require authenticated administrator privileges. In the optimal case, you will be asked to authorize developer privileges only once, and that authorization will be stored in your Keychain."

The "optimal case" certainly does not apply here, as the query appears every time I log out and log in again. It appears, though, that the problem can be circumvented by giving the non-admin user membership of a certain system group:

sudo dscl . append /Groups/_developer GroupMembership patrick


Precompiled Prefix Header

Information in this chapter is taken from the chapter "Using a Precompiled Prefix Header", section "Reducing Build Times", article "Xcode Build System Guide" (located under ADC Home > Reference Library > Guides > Tools > Xcode).


A "precompiled prefix header" is a header file that is

  • precompiled
  • included automatically at the beginning of each file of a target
  • there can be only one such file per target
  • but one file can be shared among multiple targets or projects


The header file can be shared among multiple targets, or even projects. Because of this, and because different targets/projects may contain source files written in different programming languages, it is recommended that the precompiled prefix header contain appropriate guard macros, such as this:

#ifdef __OBJC__
  #import <Cocoa/Cocoa.h>
#endif

#define MY_CUSTOM_MACRO 1
#include "MyCommonHeaderContainingPlainC.h"


To configure Xcode to use a precompiled prefix header, change the following build settings of the appropriate target, or maybe even of the entire project:

  • GCC_PREFIX_HEADER (Prefix Header). Change the value of this build setting to the project-relative path of the prefix header file
  • GCC_PRECOMPILE_PREFIX_HEADER (Precompile Prefix Header). Make sure that this option is turned on.


Note: When the Xcode wizard creates a new project, it helpfully adds a precompiled prefix header to the project which imports <Cocoa/Cocoa.h>. Unfortunately the header is not used because the appropriate build settings are disabled.


Adding a doxygen target to a project

Documentation about managing targets can be found in "ADC Home > Reference Library > Guides > Tools > Xcode > Xcode Build System Guide".

The following procedure adds Doxygen support to a project:

  • Prepare the project with these steps
    • Add a doxygen folder
    • Inside that folder, generate a template Doxygen configuration file (named Doxyfile) like this:
doxygen -g
    • Inside Xcode, add the Doxyfile to the project so that you can edit it conveniently in the Xcode editor
    • Manually modify the following options within the Doxyfile:
PROJECT_NAME     = <the project name>
FULL_PATH_NAMES  = NO
EXTRACT_STATIC   = YES
INPUT            = ..
FILE_PATTERNS    = *.h *.m
RECURSIVE        = YES
EXCLUDE_PATTERNS = */.git */build
GENERATE_LATEX   = NO
HAVE_DOT         = YES
DOT_PATH         = /sw/bin
  • Create the new target in Xcode
    • Select "Project > Add Target" from the menu. This opens the "New Target" wizard.
    • From the category "other", select the target type "Shell Script Target"
    • On the next page specify these properties:
      • Target name = doxygen
      • Add to project = the project
    • Click the "Finish" button to create the target
  • Configure the target
    • Find the new target in the Group Tree pane, then expand the target
    • A build phase entry named "Run Script" appears; open the Info window for the build phase entry
    • The actual shell script can be added on the "General" tab. This is what I usually put in:
# change directory because Doxyfile is configured with a relative input path ".."
cd doxygen

# clean the directory
rm -rf html

# generate docs
/sw/bin/doxygen Doxyfile

# open the html documentation
open html/index.html


Adding unit tests to a project

Overview

The xUnit unit testing suite has been ported to Objective-C by Sen:te, under the name of OCUnit. At some time in the past, Apple has started distributing OCUnit as an integral part of XCode.


Apple distinguishes two types of tests:

  • Logic tests: This is the traditional type of unit tests where "only" specific classes are tested in a rather narrow environment. In iPhone development, logic tests are built using the iOS Simulator SDK, but they are run outside of the simulator, right after Xcode has finished building the unit test target.
  • Application tests: This type of unit test focuses on exercising the application as a whole. In iPhone development, application tests are built into a test bundle that is then embedded into the application bundle and executed on the device itself. This allows to run tests in a realistic production environment, including tests that exercise code that requires hardware capabilities of the device.


References


Logic tests

Create a top-level folder named "test" that will contain all test-related files. Add the following subfolders

  • src = contains unit test source code files
  • resource = contains resources required by unit tests
  • testdata = contains other test data files


Add new unit test target

  • Select Project > New Target...
  • The target type is "Unit Test Bundle" and can be found either under the category "Cocoa Touch" or under the category "Cocoa"
  • Select the category that matches the project type
  • Target name = "Unit tests"


Modify the new target's build settings

  • Base SDK = Remove custom value so that the target inherits the project-wide setting
  • Header Search Path = ./src (since the test sources are completely separated from the normal application sources, we don't want to deal with relative path issues for each #import statement; the application can be seen as third party software from the point of view of the unit test bundle)
  • Precompile prefix header = Yes
  • Prefix Header = src/Prefix.pch (or whatever file is the prefix header of the project)
  • Add all classes for which test cases are written to the unit test bundle
    • An alternative (and preferrable) approach would be to build the application as a static library, and then to link the unit test bundle against that library
    • Unfortunately this approach cannot be chosen because I have not yet found a way how to link the real application against the static library


Info.plist file

  • When the new target was created, Xcode automatically added an Info.plist file for that target to the project (e.g. Unit tests-Info.plist)
  • The file is located in the project's top-level folder, but we want to move it to test/resource
  • Update the build setting (under "Packaging") that refers to the file (e.g. test/resource/Unit tests-Info.plist)
  • Make sure that the file is not copied


Add a test case file

  • Context menu for test/src > Add > New File... > Cocoa Touch Class (or Cocoa Class) > Objective-C test case class
  • File name = does not matter
  • Targets = "Unit tests" (not the project's main target!)


Test case characteristics

  • File and class name are irrelevant
  • Class must inherit from SenTestCase
  • Test method name must begin with test (lower case), the method must have no parameters and return void


Running tests

  • Building the target automatically runs the unit tests
  • The target has an additional "Run Script" phase which invokes
"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests"


Application tests

TODO


The Xcode build system

Compiling projects from the command line

To build an Xcode project on the command line, you use the utility

xcodebuild

Also see the man page. Some examples

# List all targets, and all configurations of a project
xcodebuild -list
# Use -project if there are multiple .xcodeproj in the current working directory
xcodebuild -project Foobar.xcodeproj -list

# Build the first target in the project, using the default configuration
xcodebuild
# ditto, but build the active target (the active target is the one selected in the GUI)
xcodebuild -activetarget
# ditto, but build target "foo"
xcodebuild -target foo
# ditto, but build all targets
xcodebuild -alltargets

# ditto, but use the active configuration for the build (the active configuration is the one selected in the GUI)
xcodebuild -alltargets -activeconfiguration
# ditto, but use the configuration "foo" for the build
xcodebuild -alltargets -configuration foo

# Perform the action "clean" instead of the default action "build"
xcodebuild -alltargets clean
# Perform the actions "clean" and "install" (build + install)
xcodebuild -alltargets clean install

# List all available SDKs
xcodebuild -showsdks
# Use SDK "iphonesimulator4.2" for the build. In the Xcode GUI this SDK is displayed as "Simulator"
xcodebuild -sdk iphonesimulator4.2
# ditto for SDK "iphoneos4.2". In the Xcode GUI this SDK is displayed as "Device"
xcodebuild -sdk iphoneos4.2

# Build only for architecture "armv6" (the default is to build for all architectures)
xcodebuild -sdk iphoneos4.2 ARCHS=armv6
# Build for multiple architectures
xcodebuild -sdk iphoneos4.2 ARCHS="armv6 armv7"

Useful links:


Details about the build system

Useful links:


The Xcode build system is based on the concept of "targets" - not exactly a surprising thing.

  • A target is composed of a series of build phases
  • Each build phase performs an action on one or more files that are part of the target
  • Example build phases for a simple "Cocoa Application" target
    • Copy bundle resources
    • Compile sources
    • Link binary with libraries
  • The target type usually determines which build phases there are (e.g. a "Shell Script" target has different phases than a "Cocoa Application" target)
  • To modify the build behaviour, a large number of "build settings" can be made, usually either on the project or the target level
  • Examples for build settings are compiler or linker flags, which architectures to build, etc.
  • List of sources of build settings, and their precedence
    • xcodebuild command-line flags
    • The target
    • The project
    • Xcode application preferences
    • Xcode's built in defaults
    • The user environment (e.g. ~/.MacOSX/environment.plist)
  • Build configurations (e.g. Debug, Release) are collections of build settings


Build configuration files

  • Build settings by default are stored in the Xcode project, but they can also be placed in one or more external files called "build configuration files"
  • These files usually have the extension .xcconfig
  • One useful scenario for .xcconfig files is if multiple projects should share a set of common build settings
  • The settings in an .xcconfig file usually relate to a single build configuration
  • An .xcconfig file is a simple text file with property/value pairs; e.g.
ZERO_LINK = NO
GCC_OPTIMIZATION_LEVEL = -O0
GCC_GENERATE_DEBUGGING_SYMBOLS = YES
  • The build configuration file must be added to a project in order to become available within the project
  • Once the file is part of the project, a build configuration (e.g. Debug) can be based on the file -> this can be done inside the appropriate Info Inspector window
  • The snippet from the Xcode project's .pbxproj file looks like this:
baseConfigurationReference = 65F688F1A6A7B5ADEF3706EA /* Pods-Little Go.debug.xcconfig */;
The string 65F688F1A6A7B5ADEF3706EA is merely a reference to the actual .xcconfig file that is defined elsewhere in the .pbxproj


Build rules

  • Every target has a set of associated build rules -> refer to the target's Info Inspector window
  • Each rule tells the build system how to process a certain file type
  • For instance, the "System C" rule defines that "C source files" should be processed using "GCC System Version"
  • Most of the time, a target will have only the built-in rules, but it is possible to define custom rules
  • File types in a custom rule are defined either by selecting from built-in file types, or via a file pattern (e.g. *.foo)
  • Processors in a custom rule are defined either by selecting from built-in processors, or via a custom command or script
  • In addition to file type and processor, the rule must also specify the output file(s) - presumably so that the build can check whether a target is up-to-date or needs processing


Useful build settings

  • ARCHS
    • Determines the architecture(s) of the build
    • $(NATIVE_ARCH) refers to the architecture of the machine that the build is taking place on
    • Other architectures are: i386, ppc, armv6, armv7
    • To make a universal build, specifiy ARCHS="ppc i386"
  • SDKROOT


Customize new project

Change default project structure

When Xcode creates a new project, the project structure is not entirely to my liking. Changing things is not entirely straightforward, therefore this section documents a few things.


Regular sources

Regular sources (.h and .m files) can simply be re-added after they have been physically moved. Xcode will automatically add .m files to the build target, while .h files are automatically excluded.

Note: The file main.m counts as a regular source file.


Precompiled header

The project-specific precompiled header file Foobar_Prefix.pch can be simply re-added after it has been physically moved. It should not be added to the build target. In addition, the following references have to be updated:

  • Build target, all configurations: Under "GCC 4.2 - Language", the property "Prefix header" must be updated (to find references, you may want to search for "prefix")


IB files

Interface Builder (IB) files (.xib files) can be simply re-added after it has been physically moved. They should be added to the build target.


Info.plist

The Info.plist file can be simply re-added after it has been physically moved. It should not be added to the build target. In addition, the following references have to be updated:

  • Build target, all configurations: Under "Packaging", the property "Info.plist" must be updated (to find references, you may want to search for "Info.plist")


Debugging with Xcode

How to debug EXC_BAD_ACCESS

The reason for getting an EXC_BAD_ACCESS error message usually is a message being sent to an object that is already deallocated. If the cause for the problem is not immediately apparent, the situation is often hard to debug since you can't inspect the object in question. The solution are "zombies".

When this feature is enabled in Xcode, a dummy object (a zombie) is kept at the memory location of a released object. Inspection of the dummy object often provides valuable clues as to what happened. Enabling of zombies in Xcode 4 is very easy:

  • Product > Edit Scheme
  • Select the "Run" action
  • On the "Diagnostics" tab, check the "Enable Zombie Objects" checkbox


Note: Don't forget to disable zombie support after you are done with debugging.


Instruments

Overview

The easiest way to run Instruments is from within Xcode:

  • Select the menu item "Product > Profile"
  • When Instruments launches you are prompted with a selection of trace templates that are appropriate for the target you are profiling (e.g. you won't get iOS templates if you are profiling a Mac OS X application)


Leaks instrument

A useful option is "Gather Leaked Memory Contents":

  • Select the "Leaks" instrument
  • In the detail view a number of categories appear on the left-hand side
  • One of them is the category "Leaks configuration"; below that category you will find a checkbox labelled "Gather Leaked Memory Contents"
  • Click the checkbox
  • To actually see the memory content, select the menu item "View > Extended Detail"
  • When you select a leak you will now see a stack trace and a hex view of the memory content that was leaked


Leaks from the command line

  • Run the program from within Xcode
  • Execute the following command in Terminal.app
leaks <pid> | less
  • You will get a nice overview of all leaks
  • Most important: You will get an ASCII representation of the hexdump


Allocations instruments

Use this instrument to find memory that is "lost" instead of leaked. Similar to leaks, lost memory is allocated but never freed, but the difference is that there is still a reference to the memory block hanging around somewhere. This is why the Leaks instrument won't find this type of problem. Possible sources for lost memory

  • Retain cycles where two objects reference each other, but nothing else refers to those two objects
  • Forgotten dictionary entries
  • Probably more, but the two examples above are what comes to mind immediately


Notes about using the Allocations instrument

  • It must be configured to use the "Created & Still Living" option
  • Bring the application into a stable state where no allocations are happening
  • Perform an operation
  • Undo the operation, or use some other means to bring the application back into the stable state from where you started
  • Possibly wait some time until your app activity has died down
  • Memory usage should now be exactly the same as when you started the initial operation
  • In addition the following might apply to the application
    • Apply a time filter to the Allocations track so that only allocations in the time between the start and the stop of your operations are taken into account
    • You should now see zero net values in the columns "Live Bytes" and "# Living", i.e. all objects that were created in the filtered timespan have also been destroyed
    • Whether or not this works depends, of course, on how the application is implemented - if the application replaces existing objects with new objects, net values will not be zero and this type of examination will not provide useful results.


Interface Builder (IB)

This chapter contains various tidbits related to working with Interface Builder (short: IB). The main ressource for IB-related documentation is the "Interface Builder User Guide" [1]


What is a .nib file?

  • nib = NeXT Interface Builder,
  • A .nib file contains an archived version of a set of GUI elements, and/or other non-visual elements (such as controllers) that are responsible for managing the GUI elements
  • The content of a .nib file is edited with the Interface Builder application
  • When a .nib file is loaded, its content is unarchived and the objects defined within the archive are re-created exactly as they were present when the file was last edited with Interface Builder
  • IB version 3 introduced a new file format, using the extension .xib, which is functionally identical to .nib, but stores its content in a flat file. This is much more suitable for storing the file in SCM systems, and makes it also easier to process the file with tools like diff.


What is the "File's Owner"?

  • The "File's Owner" object is the "controller" object that is responsible for (the owner of) the contents of the .nib file
  • When the .nib file is loaded, the File's Owner object must already exist
  • All the other objects inside a .nib file are created at the time that the .nib file is loaded
  • When the .nib file is loaded, the nib loading code restores any outlet connections defined in the .nib file
  • The File's Owner object thus becomes the link between the .nib file and the rest of the application code
  • The File's Owner object of the application's main .nib file is the NSApplication object


What is an outlet?

  • Represents a member variable of the class that the outlet belongs to
  • An outlet has a type
  • An outlet is defined in the "Classes" view, in the Info window -> Attributes
  • In the "Instances" view, control-drag from an instance A to a different instance B; this allows to connect an outlet within instance A to the instance B (if the outlet has a type that matches the type of instance B); when the .nib is loaded, the outlet's member variable of instance A will have a reference to instance B


What is an action?

  • Represents a method of the class that the action belongs to
  • The method signature must look like this: (void)myAction:(id)sender;
  • With Java, it is important that the name of the argument is "sender"
  • An action is defined in the "Classes" view, in the Info window -> Attributes
  • In the "Instances" view, control-drag from an instance A to a different instance B (possible only if A is a control); this allows to connect the control to one of the target's (instance B) action method


How to define a button that should react to the Enter, Escape or any other key being pressed:

  • Info window -> Attributes -> Equiv


How to define the first element that should be active when a window is opened:

  • Select the NSWindow instance
  • Control-Drag from the instance to the element that should be active
  • Info window -> Connections -> Outlet -> initialFirstResponder
  • Click the "connect" button


How to define tabbing between two elements:

  • Select first element
  • Control-Drag to the second element
  • Info window -> Connections -> Outlet -> nextKeyView
  • Click the "connect" button


How to define the NSApplication delegate:

  • Select the "File's Owner" instance in the main .nib; this is the NSApplication object
  • Connect the outlet delegate of the "File's Owner" instance to the desired delegate instance
  • Some of the messages that the delegate receives are sent by NSApplication itself (e.g. applicationOpenFile), others are sent by the application's default notification centre (the delegate is automatically registered with the default notification centre if it implements methods that correspond to the delegate messages)
  • The documentation for NSApplication lists the 20+ delegate methods


References

  1. Interface Builder User Guide