Running mogenerator from Xcode

If you do any Mac development with CoreData and don’t know about Jonathan Rentzsch’s mogenerator, you should definitely check it out. Getting started with mogenerator has all the info you need to download and install it (update: see his latest info about mogenerator 1.1). mogenerator saves a bunch of work, but it’s an early release so at this point there’s no Xcode integration. And although I’m quite fond of the Terminal, I don’t like bouncing over to it whenever I need to regenerate my classes. Thus, it wasn’t long before I decided to see if I could at least add mogenerator to Xcode’s script menu. It turned out to be a little more involved than I expected…


The author of mogenerator is working on better Xcode integration for a future release, but I figured it was likely that I would still want to invoke mogenerator manually before compile time, if only to get the full advantages of code completion when I add new properties. My goal was to create something that would eliminate having to write any project specific commands or configuration. I wanted a single menu/key command that would run mogenerator against the data models in my current project.

Via AppleScript I found I could easily determine which project was open in Xcode, along with its path. The dictionary also said I could get the project’s data models — but didn’t work that reliably. Fortunately, the project path was enough, because it only requires a couple simple Unix commands to find all the data model files in the folder and run mogenerator against them.

The next step was to read through Using Scripts To Customize Xcode. I won’t go through the details, but ultimately I ended up with a menu item stuck right onto the end of the Design menu that let me run mogenerator. Cool, huh?

Xcode's Design menu

Except it didn’t work — you’ll notice that it’s inactive in the screenshot. That’s because, so far as I can tell, Xcode assumes that all custom scripts modify source code, so they’re only active when a source file is open — and the data modeler doesn’t count. When I first installed the script, it worked so long as I clicked on an *.h or *.m document before invoking it, but I recently decided to experiment with using TextMate as my primary editor, so now I never have a source file active in Xcode.

So I had to find a plan B. I turned to the system script menu — the one that lives as a status menu on the right side of your toolbar, which you activate via the AppleScript Utility. Installing the script there works pretty well, except that I haven’t been able to figure out how to assign it a keyboard shortcut.

Script menu

In any case now I can easily run mogenerator against all the data models in any open Xcode project. No trips to the Terminal nor project specific configuration needed. Here’s the script.

AppleScriptRun mogenerator.scpt

(*
 * ----------------------------------------------------------------------------------------------
 * Run mogenerator script
 * Processes all .xcdatamodel files in whatever Xcode project is active
 *
 * by Scott Guelich 12-27-2006
 *
 * INSTALLATION:
 * Drag this script to ~/Library/Scripts/Applications/Xcode
 *
 * Note: The script will appear in the system-wide Script Menu,
 * which is different than the script menu that appears left of Help in Xcode.
 * If you don't see another Script Menu, Run the AppleScript Utility and
 * check "Show Script Menu in menu bar".
 *
 * mogenerator was created by Jonathan "Wolf" Rentzsch
 * and is available at http://rentzsch.com/code/mogenerator
 * ----------------------------------------------------------------------------------------------
 *)

-- Override these properties as needed on your system
property mogeneratorPath : "/usr/local/bin/mogenerator"
property showConfirmation : true

-- ----------------------------------------------------------------------------------------------

tell application "Xcode"
	set mogeneratorPathCheck to ""
	activate
	try
		set mogeneratorPathCheck to do shell script "
			if [[ -e \"" & mogeneratorPath & "\" ]]; then
				echo \"" & mogeneratorPath & "\";
			else
				echo `which mogenerator`;
			fi
		"
		if mogeneratorPathCheck starts with "no mogenerator" then
			display dialog "Could not find mogenerator script on your system. " & ¬
				"Please edit this script and set the mogeneratorPath property" ¬
				buttons {"Cancel"} default button "Cancel" with icon 1
		end if
		set mogeneratorPath to mogeneratorPathCheck

		set projectPath to the POSIX path of ¬
			(the project directory of the active project document as text)

		set output to do shell script "
			cd \"" & projectPath & "\";
			find . -name '*.xcdatamodel' -print | sed 's/^../  /';
		"
		if output = "" then
			display dialog "Could not find any xcdatamodel files in your current project. " & ¬
				"Is the right project active in Xcode? Also, note this script doesn't support " & ¬
				"referenced files that don't live in your project folder." ¬
				buttons {"Cancel"} default button "Cancel" with icon 1
		else if showConfirmation then
			display dialog "Run mogenerator against the following files?\\n" & output with icon 1
		end if
		set output to do shell script "
			cd \"" & projectPath & "\";
			find . -name '*.xcdatamodel' -print0 | xargs -0 \"" & mogeneratorPath & "\" -model
		"
		if output ≠ "" then
			display dialog "Unexpected error invoking mogenerator:" & output ¬
				buttons {"Cancel"} default button "Cancel" with icon 1
		end if
	on error number errorNumber
		if errorNumber is equal to -128 then -- user cancelled, do nothing
		else if errorNumber is equal to -1728 then -- can't get POSIX path to Xcode project
			display dialog "Unable to get the current project's path in Xcode. " & ¬
				"Are you sure you have a project open?" ¬
				buttons {"Cancel"} default button "Cancel" with icon 1
		else
			display dialog "Unknown error running mogenerator: " & errorNumber ¬
				buttons {"Cancel"} default button "Cancel" with icon 1
		end if
	end try
end tell

Update: You can also download the code for the original non-working solution (actually, it might work well enough if you use Xcode as your primary editor). To me it seems like a bug that the menu item for an Xcode user script which specifies that it doesn’t receive text input (via PBXInput=None) becomes inactive unless a text document is open. I filed it as rdar://problem/4905038 (rdar:// urls).

2 Responses to “Running mogenerator from Xcode”


  1. 1 Adam Chester Jan 2nd, 2007 at 12:41 pm

    Scott,
    Many thanks for sharing your script. Jonathan’s tool is excellent, and this just makes it a little sweeter.
    Adam

  2. 2 Paul Robinson Oct 2nd, 2007 at 5:28 pm

    This is very useful, thanks!

    You can add a keyboard shortcut by using Daniel Jalkut’s FastScripts. Works perfectly for this kind of problem:

    http://www.red-sweater.com/fastscripts/

Leave a Reply