Matt Legend Gemmell Modesty is Lying

Mac OS X Cocoa and iPhone Development Services available at Instinctive Code.
Favorites icon
Favorites for iPhone
Speed-dial with style.
Mac OS X Cocoa and iPhone Developer for hire
Sheercore Comics - Geeks with Swords!

Other Pages

Categories

Posted
20 May 2008 @ 12pm

Categories
Development, Source

Tags
, , , , , , , , , ,

MGTemplateEngine – Templates with Cocoa

MGTemplateEngine is a native Cocoa system for generating text output based on templates and data. It’s a close cousin of systems like Smarty, FreeMarker, Django’s template language, and so on.

It’s ideal for Cocoa apps needing to generate text output using variable-substitution (with looping and/or conditional logic), including creating HTML pages (or for apps with WebKit-based UIs), generating invoices or other printable templates, mail merge, data export or any number of other things. It’s also great (in combination with WebKit) for letting your users create themes/styles for your application.

Here’s a sample template, and the corresponding sample output (with appropriate variables fed to the template engine).

The default syntax for markers (functions or language-constructs) is:

{% for 1 to 5 %} foo {% /for %}

and the default syntax for variables/expressions is:

{{ foo.bar | uppercase }}

The pipe-character indicates a filter is being applied; i.e. the value of “foo.bar” will then be fed to the “uppercase” filter before being displayed. You can apply filters to markers as well as variables.

The marker, variable and filter delimiters are completely customizable, so you’re not stuck with the defaults if you prefer different syntax. You can also write your own markers (language constructs/functions), filters (data-formatting functions) and even your own matchers (advanced; see documentation).

MGTemplateEngine offers the following features:

  • Native Cocoa implementation. It doesn’t use the Scripting Bridge or any external runtimes/frameworks, and as such the core engine itself has no requirements other than Mac OS X Leopard.
  • Very customizable. It’s very easy to define new markers (like functions or language-constructs) and new filters (data-formatting capabilities). You can also freely change the syntax of the markers and expressions to suit your own tastes, or to mimic your favorite other templating system.
  • Delegate system to keep you informed. MGTemplateEngine can optionally inform a delegate object of significant events during processing of a template, including beginning/ending blocks, or encountering errors.
  • Global and template-specific variables. You can define a set of variables which exist for the lifetime of the engine, and also specify variables which only apply to a certain template.
  • Access variables using familiar Key-Value Coding (KVC) key-paths, with enhancements. For example, if you had an NSDictionary containing an NSArray for the key “foo”, and that array contained 5 NSDictionaries, each of which had an NSString for the key “bar”, you could access the value of the fifth dictionary’s “bar” object using this syntax: foo.4.bar (remembering that array indices are zero-based!)

MGTemplateEngine requires Mac OS X 10.5 (Leopard) or later, and can be downloaded from my public Subversion repository at http://svn.cocoasourcecode.com/MGTemplateEngine

You can also read the documentation here:

Get in touch with me via email (details here) if you have feature requests or bug reports, and if you use MGTemplateEngine in your app, be sure to let me know so I can link to you. General praise and thanks can go into the comment box below. ;)

(Finally, and in a small voice, remember the donation link on my Cocoa source code page…)

I plan to continue improving MGTemplateEngine in the future, and I hope you’ll find it useful!

Update: Added the % operator to the if marker. Syntax is if x % y, which as you’d expect returns true if x is not evenly divisible by y, and false otherwise. Very handy within for loops for creating alternating rows, as shown in the (also updated) sample template.

Also added the cycle marker, which takes any number of arguments and will output the next one in the list each time the tag is encountered. Great within loops to cycle between a set of values (e.g. again for alternating row colors). Sample template updated to show this.


10 Comments

John Haney
20 May 2008 @ 12pm

Matt, this looks quite useful… Have you considered a different scheme other than this big function?… Your original function has so many output parameters of which I would expect typically only one or two to be used at a time seems clunky to me.

- (NSObject *)markerEncountered:(NSString *)marker withArguments:(NSArray *)args inRange:(NSRange)markerRange blockStarted:(BOOL *)blockStarted blockEnded:(BOOL *)blockEnded outputEnabled:(BOOL *)outputEnabled nextRange:(NSRange *)nextRange currentBlockInfo:(NSDictionary *)blockInfo newVariables:(NSDictionary **)newVariables;

And instead pass in one object representing the engine doing the parsing with the input…

- (NSObject *)markerEncountered:(NSString *)marker withArguments:(NSArray *)args inRange:(NSRange)markerRange byParser:(MGParser *)parser

MGParser would supply the API functions which the MGMarker could call to “return” the object(s) found.

Just curious if this was a conscious design choice.

Thanks,
- John Haney


Matt Legend Gemmell
20 May 2008 @ 12pm

Hi John,

It just evolved that way; it’s certainly something I’m considering refactoring a bit in future, because it’s quite a large method sig.


Jeff Nichols
20 May 2008 @ 1pm

Ooohhh… saucy! I’ve been wondering when something like this would be made. I haven’t needed it yet, but I’ve been on the verge and I’m sure there will come a day. I’m glad you count Freemarker as an influence, as it’s been my favorite templating engine to this point.

Thanks Matt!
Jeff


Tony Arnold
20 May 2008 @ 2pm

Awesome stuff, Matt. I’ve got a project in mind that this will suit perfectly – thanks!


John McLaughlin
20 May 2008 @ 5pm

Hi Matt,

I haven’t had a chance to download it (yet) but this looks perfect — I’ve had to roll quite a few of my own, very imperfect, templating systems in the past but this looks more robust than what i’ve used before.

Thanks


Stuart Connolly
20 May 2008 @ 6pm

Very nice, Matt. You seem to provide an endless supply of source code and ideas for apps.


Tyson Weihs
12 June 2008 @ 4pm

Do you have a build that works with UIKit/iPhone SDK? Would very much like to use this for iPhone dev, but there are dependencies on libs that aren’t available on the iPhone.


André Neves
7 August 2008 @ 12am

This is great stuff, Would love to see it ported/built for the iphone. Any news on that?


Matt Legend Gemmell
21 October 2008 @ 7pm

Hi all,

MGTemplateEngine has been updated in the repository so that it should now build on iPhone. If you use the ICUTemplateMatcher matcher (the default one used in the sample code), you should be able to link against the libicucore library for iPhone builds. That library is here:

/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/
iPhoneOS2.1.sdk/usr/lib/libicucore.A.dylib


Puddn
29 October 2008 @ 2am

Matt, been looking at the engine and I can’t figure one thing out. Is this possible?
{% var exists/has data != nil %}

{% /if %}

Thanks


Leave a Comment

ImageCrop updated HUD controls framework for Leopard