When designing a Core Data data model for your Xcode projects, you can choose to create Objective-C object wrappers for your entities, so that you can profit from type-safe code. The normal, tedious, workflow for this is that you select each entity from the model designer, select all of its attributes and relationships, Ctrl-click it and from the contextual menu first select âCopy Obj-C 2.0 Method Declarations To Clipboardâ, paste it into the appropriate class header file, then do the same thing for the method implementations in the class implementation file. Waaaaaay too much work. Not to mention the manual copy-pastes are really hard to keep in sync once you start adding functionality to these class files, since you donât want to overwrite those additions, but you want to keep replacing everything else.
Meet mogenerator ¶
Fortunately, there is a great way for automating this process, using mogenerator. The tool can be downloaded as a DMG installer (Aral Balkanâs blog mentions a workaround for older Xcode versions, but for Xcode 3.1.3 it worked out of the box for me), or you can checkout the sources from github and build it yourself.
The mogenerator command line tool eases this generation process by reading the
*.xcdatamodel
file and generating both class files and intermediate class
files for each entity. The intermediate classes (called machine classes) are
continuously overwritten by subsequent regenerations, so you should never edit
the contents of these files. The actual model object classes (called human
classes) inherit from those intermediate classes with a default empty
implementation, allowing for all manual extensions.
For example, when you design a model with two entities Foo and Bar, mogenerator can be invokes as follows:
$ mogenerator -m MyDocument.xcdatamodel -M Entities -H Model
The flag -m
sets the input model file, while -M
and -H
specify the output
directories where the machine and human classes should be generated
respectively.
This does a few things:
- In the Entities subdirectory, there will be generated header and
implementation files for NSManagedObject subclasses called
_Foo
and_Bar
; - In the Model subdirectory, there will be generated classes called
Foo
andBar
ârespective subclasses of_Foo
and_Bar
. These are only created if not available yet. Otherwise, they are left as is.
Wrapping it up ¶
The trick of how mogenerator works is that you can run the script as often as you want. After every change in your model, youâll want to re-run the generation again to update the machine classes. You could easily leave Xcode, switch over to Terminal and issue the command above. But youâll get quite tired of that after a few times.
Therefore, Iâve written a custom user script that can be added to Xcode (see figure), which does the following:
- You can configure the output directories in the first lines of the script. There is no per-project configuration, so choose them as you would like to use them with all your projects;
- Mind that these generated files are not automatically included in your Xcode project. Drag them there once and ideally put the machine generated classes into a group under âOther resourceâ, so you never have to see them again. Whenever you add a new class to your model, new files will be generated, so again you must drag the new files to reference those, of course!
- The script can be run with any file in the project opened. It starts out with
that file and walks up the directory tree to search for your Xcode project.
If found, it executes all the rest from your project directory. (Suggestions
are welcome, I could not find a better implementation since a variable like
%%%{PBXProjectPath}%%%
does not seem to exist.) - It invokes mogenerator to generate all model classes for the project. It is smart enough to detect whether you are using Brian Websterâs BWOrderedManagedObject in your project. If so, your generated machine classes will inherit from BWOrderedManagedObject instead of NSManagedObject.
To add this script to Xcode, open the menu Scripts (the icon) > Edit User Scripts⊠Click the â+â-button on the bottom-left and select âNew shell scriptâ. Set the values for Input, Directory, Output and Errors as in the screenshot above, then copy-paste the script below into the code window. Add a nice keyboard shortcut to this action to top it off :-) Iâve chosen â„âG for this.
Please feel free to leave any comments if this helped you.
#!/bin/sh # # Automatic (re)generation of model classes for all *.xcdatamodel files. # Written by Vincent Driessen # # You are free to use this script in any way. # The original blog post is http://nvie.com/archives/263 # # Define output directories MACHINE_DIR="Entities" MODEL_DIR="Model" # Look for the Xcode project directory for this file cd `dirname "%%%{PBXFilePath}%%%"` while [ `ls -d *.xcodeproj 2>/dev/null | wc -l` -eq 0 ]; do cd .. if [ "`pwd`" = "/" ]; then echo "No Xcode project found." exit 1 fi done echo "Project directory is `pwd`" # # Check to see whether the base class is just a default (NSManagedObject) or # maybe Brian Webster's excellent BWOrderedManagedObject. # http://fatcatsoftware.com/blog/2008/per-object-ordered-relationships-using-core-data # # NOTE: # The check really is quite arbitrary: if there exists a file called # BWOrderedManagedObject.h somewhere below the project root directory, we # assume that we want to use this as the base class for all generated classes. # EXTRA_FLAGS= if [ `find . -name BWOrderedManagedObject.h | wc -l` -gt 0 ]; then EXTRA_FLAGS+="--base-class BWOrderedManagedObject" fi # Generate the model classes using mogenerator for model in `find . -name '*.xcdatamodel'`; do # The output directories have to exist, so create them mkdir -p "${MACHINE_DIR}" "${MODEL_DIR}" mogenerator ${EXTRA_FLAGS} -m "${model}" -M "${MACHINE_DIR}" -H "${MODEL_DIR}" done