PyObjC Xcode Templates

Author: Bob Ippolito
Contact: bob@redivi.com

The PyObjC Xcode Templates offer an alternative to developing applications "by hand" using py2app, as described in the tutorial. As of PyObjC 1.3.1, these templates are py2app based, so there is no longer a technical reason not to use them.

Contents

Installing

If you have installed PyObjC 1.3.1 or later using the installer, then the Xcode templates are already installed.

If you have installed any version of PyObjC prior to 1.3.1, then you may have old Xcode templates installed. These Xcode templates named "Cocoa-Python Application" and "Cocoa-Python Document Based Application" should NOT be used, and it would be wise to remove them. They can be found here:

/Library/Application Support/Apple/Developer Tools/Project Templates

To install the templates manually, simply copy (or link) them into this Project Templates folder.

Notes

This change will be specific to your user account, and will take effect globally, which may or may not be a good thing.

Groups

The PyObjC Xcode templates use py2app to build applications, but they parse the .xcode project file to determine how they should be built, rather than directly in the setup.py. The parser, in PyObjCTools.XcodeSupport, gives special meaning to several groups. If these groups are renamed or removed, your project may not build correctly!

Main Script:

This group should contain exactly one file, the main script of your application. The default main script in the template generally does not need to be changed.

If you need to ensure that additional code is imported, simply place it in the Classes group. You shouldn't need to modify your main script.

ONLY the main script should be in this group.

Resources:

Every file in this group goes into the Resources folder inside of your application bundle.

Any .nib files that are in this folder will be parsed with PyObjCTools.NibClassBuilder.extractClasses by the main script before any modules in the Classes group are imported, and before the run loop is started. You should not need to call extractClasses manually in your code.

Source code should not go in here.

Classes:

Modules in the classes group will be imported by the main script in the order that they appear in the Xcode project, after all classes are extracted from the nibs.

Every Python module in this group is guaranteed to be scanned by py2app for dependencies.

Other Sources:

This group is not actually special. You may put anything you want in this group. It is used by the templates to store files and source code that do not fit into any of the above categories, such as the Info.plist and the setup.py.

setup.py:

This is the script that is actually used to build your project. It may also be used from the command line either directly or via xcodebuild. Read the file for more instructions. This script must not be renamed or removed.

If you need to customize the py2app or distutils build process, you should modify the setup_options dict before the setup(...) function is called.

Info.plist:

When present, this file is used as a template for your application's Info.plist. If you rename or delete it, then it will not be used and plist will be generated by py2app. For information about what can go in this plist, see Runtime Configuration: Property List Key Reference.

Targets

Development:

This target will use py2app --alias build mode. Executables built with this mechanism are produced very quickly, and use the sources in-place via symlinks and sys.path manipulations. These executables are not redistributable, much like development executables produced by Xcode when using Zero-Link.

Deployment:

This target will use py2app's default build mode, --standalone. This will create a standalone bundle out of your application that is redistributable. Everything that py2app determines to be needed by your application will be included in the executable, including Python itself, extensions you use, and dynamic libraries or frameworks that are linked to by these extensions.

If you are using a Python distributed by Apple, then it will be built in --semi-standalone mode. This means that Python and its standard library will not be included in the application.

Using the Deployment target does not automatically imply that you are using the Deployment build style. This is only relevant when using the PyObjC Mixed Application template, or are otherwise using the same project to compile non-Python source code. To change the current build style, Get Info on the project. The build style has no effect on Python code.

Custom Executable

The custom executable enables for your built application to be run from Xcode.

If you rename your main script or fiddle around with your Info.plist, the path to your application may change and this will no longer work. If that is the case, use Get Info on the custom executable and change the Arguments to point to the correct path.

By default, executables are launched with the USE_PDB environment variable set for both Development and Deployment targets. This turns on verbose stack traces whenever an exception crosses the PyObjC bridge, and will drop you at a pdb prompt in the console when an uncaught exception occurs.

Other useful environment variables that you can set, such as NSZombieEnabled, as well as all kinds of other debugging tricks you should know are covered in TN2124: Mac OS X Debugging Magic.

If Xcode screwed up and didn't create a Custom Executable, which is not beyond the realm of possibility, then you can create one as follows:

  1. Create a Custom Executable for /usr/bin/env (yes, Xcode is dumb)
  2. Set that it runs from the Built Product directory
  3. Use YourProject.app/Contents/MacOS/YourProject as the first (and only) argument
  4. Optionally set the USE_PDB (or any other) environment variables

Note that when debugging using gdb, you'll get a trap signal because /usr/bin/env will be execve'ing your application. Unfortunately, there's nothing we can do from the template, because Xcode can only create Custom Executables to absolute paths. However, you can probably modify yours such that it points directly to your built application after it has been built once.

Custom executables are specific to a particular user in Xcode, so anything you do to this part of the template won't be seen by anyone else unless they happen to have the same short user name as you.

PyObjC Application

This is a simple template that has a window and an application delegate.

PyObjC Document Based Application

This is template demonstrates a Document-based application written in Python. It is a simple text editor (like the TinyTinyEdit example).

PyObjC Mixed Application

This template contains both Objective-C and Python code. The Objective-C code is built as a "ProjectNamePlugIn.bundle" plug-in in a separate target. The plug-in is placed in the Resources directory of your application. A wrapper script in the Classes group, "ProjectNamePlugIn.py" can be imported from Python and will contain all of the Objective-C classes referenced in the plug-in (even if there is more than just the example ProjectNamePlugIn class).

The example class implements a simple method, -(BOOL)plugInLoaded which will always return YES. The Python application delegate creates an instances of this class, and will beep when the application has finished launching if the plug-in loaded correctly. If it didn't load correctly, you would get an exception, but that shouldn't happen :)

Note that this template doesn't have any special machinery behind it. If you find that you later need to add Objective-C code to a project that you started with one of the other templates, you can do the following:

  1. Create a new Objective-C loadable bundle target
  2. Make the Development and Deployment targets depend on this target
  3. Drag the product loadable bundle into the Resources group
  4. Create a wrapper module, that uses objc.loadBundle(...) in the Classes group (see the example in the template).

Note that switching between Development and Deployment targets does not imply that you are switching to the build style of the same name. See the Targets section for more information.