Resource code generator for iOS inspired by the Android R
resource class.
The Android R
resource class is generated by a tool called
aapt
(Android Asset Packaging Tool) and makes it possible to access resources
conveniently and safely.
For example access images like this
imageView.setImageResource(R.drawable.someimage)
or strings this
getString(R.string.hello)
.
rgen
makes it possible to access resources in a similar way on iOS by
reading the Xcode project file and generate code for resources that
will be available in the application bundle.
imageView.image = I.images.cuteSeal;
instead of this
imageView.image = [UIImage imageNamed:@"images/cute-seal.png"];
[NSDictionary dictionaryWithContentsOfFile:P.files.dogsPlist];
instead of this
[NSDictionary dictionaryWithContentsOfFile:
[[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:@"files/dogs.plist"]];
NSLocalizedString(S.cats, nil)
instead of this
NSLocalizedString(@"cats", nil)
This is very nice for several reasons
- Less error-prone as you get compile time errors for missing images, files and string keys.
- Code completion for resources
- Less and nicer looking code
Homebrew:
$ brew install --HEAD https://raw.github.com/wader/rgen/master/homebrew/rgen.rb
Compile from source:
$ git clone git://github.com/wader/rgen.git
$ cd rgen
$ xcodebuild
$ cp build/Release/rgen /to/somewhere
Note that if you compile with Xcode 4 from the IDE the binary will probably not end up in the build directory. Instead try "Show in finder" on the rgen binary under "Products".
If don't want to compile yourself there is a binary for download compiled for Mac OS X 10.6 i386 that will work on both 32 and 64 bit systems.
Usage: ./rgen [-IPSv] xcodeproject [Output path] [Target name]
-I, --images Generate I images property tree
-P, --paths Generate P paths property tree
-S, --stringkeys Generate S localizable string keys class
--loadimages Generate loadImages/releaseImages methods
--ipad Support @ipad image name scale suffix
--ipad2x Support @2x as 1.0 scale image on iPad
-v, --verbose Verbose output
If no Output path
is specified code will be generated to
Resources.m
and Resources.h
in the current dirctory.
If no Target name
is specified rgen
will merge
resources for all targets.
-I
, -P
and -S
enables code generation for
the different resources. At least one must be enabled.
--imageimages
generates two methods loadImages
and
releaseImages
for each image directory. loadimages
can be used to load and retin all images in a dirctory and below.
releaseImages
does the opposite by releasing them.
--ipad
generates image loading code that will extend the normal
@2x
suffix to also load @ipad
images if found on
iPad devices.
--ipad2x
generates image loading code that loads @2x
images as scale 1.0 images on iPad devices. If --ipad
is enabled
a @ipad
image has priority.
-v
enables verbose logging to see what rgen does.
Add a "New run script build phase" to your target. The run script should look
something like the one below. which
is used to make sure a user can
build without rgen
is installed.
$PROJECT_FILE_PATH
and $SRCROOT
will be assigned
by Xcode.
RGEN="/path/to/rgen"
which -s "$RGEN" && "$RGEN" -IPS "$PROJECT_FILE_PATH" "$SRCROOT/Classes/Resources"
Now build the target and two new files, in this case
Classes/Resources.m
and Classes/Resources.h
will be
created. Add these as source files to your project and then import
Resources.h
where you want access to the resouces classes.
Then and your good to go!
To make thing even smoother you can add a #import "Resources.h"
line
to your *_Prefix.pch
file. rgen
makes sure not to touch
the generated files if nothing has changed since last run to not trigger
unnecessary rebuilds.
Every target should generate their own resource code and header file to make sure you get compile errors if you accidently use resources not available to a target.
Each target should have a build script, for example:
TargetA
RGEN="/Users/mattias/src/rgen/build/Release/rgen"
which -s "$RGEN" && "$RGEN" -IPS "$PROJECT_FILE_PATH" "$SRCROOT/Classes/TargetA" "$TARGET_NAME"
TargetB
RGEN="/Users/mattias/src/rgen/build/Release/rgen"
which -s "$RGEN" && "$RGEN" -IPS "$PROJECT_FILE_PATH" "$SRCROOT/Classes/TargetB" "$TARGET_NAME"
Now the files TargetA.m
, TargetA.m
, TargetB.m
and TargetB.h
will be
generated. Build each target and then add the files to the corresponding
target of your project.
Unfortunately the C preprocessor does not support comparing strings
(#if TARGET_NAME == "TargetA"
etc) so if you not already have some define
to distinguish between your targets you should add a define under
"Preprocessor marcos" in your build settings. A solution is to define TARGETA
to some dummy value for target TargetA and TARGETB for target TargetB.
Now add imports to some shared header file (*_Prefix.pch
is a good place).
#if defined(TARGETA)
#import "TargetA.h"
#elif defined(TARGETB)
#import "TargetB.h"
#endif
Done!
Should work fine as long as your project does not have unsupported source trees paths. rgen uses various exported environment variables when running in a build script to figure out paths but can fallback to guessing paths based on project path.
Example:
rgen -IPS path/to/app.xcodeproj path/to/Classes/ResourcesTargetA TargetA
Will generate files path/to/Classes/ResourcesTargetA.m
and
path/to/Classes/ResourcesTargetA.h
with images, paths and string
keys found for target TargetA
- Support custome source tree paths
- Support Mac OS X applications. Autodetect via SDK (iphoneos/macosx) and generate NSImage code etc
- Read other .strings files (more then just Localizable.strings)
- Detect class name collisions
- Support document paths somehow. Specify list of known paths etc
- Rebuild dependencies seams to be calculated before starting build so you might need to build twice to use updated resources files
- Xcode plugin?
- RGEN path as user setting for shared project files. Make sure rgen is in path or use .xcconfig files?