きりかノート 3冊め

おあそびプログラミング

発表: Xcode 4のプロジェクトテンプレート (Xcode)(9/01)

ファイルテンプレートについてはある程度情報があるんだけど、プロジェクトのほうはぜんぜんだったので、自分の記憶の発掘のついでにまとめてみたのです。

だいたい資料に書いたけど、slideshareに貼ったドキュメントとかみんな読まないと思うのでこっちにもざっと。

Xcode 4のテンプレート

Xcode 4のテンプレートはおおむねこんな感じ。

  • 拡張子は .xctemplate
  • "TemplateInfo.plist"というファイルが中にあり、こいつが重要
  • 配置場所は以下のいずれか
    • 標準 /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/
    • ユーザ定義 ~/Library/Developer/Xcode/Templates/

TemplateInfo.plistのIdentifierキーの値がそのテンプレート固有のIDで、これは他のテンプレートと重複しないようにする必要がある。たとえば、テンプレート"Cocoa Application"では次のようになっている。

   // Cocoa Application.xctemplate/TemplateInfo.plist
   <key>Identifier</key>
   <string>com.apple.dt.unit.cocoaApplication</string>

インジェクション (InjectionTargets)

Xcode 3までのテンプレートとの最大のちがいとして、差分のテンプレートというのを作る・使うことができる。その機能のひとつがインジェクション(Injection)。InjectionTargetsキーに上述のIdentifierを指定すると、そのテンプレートに機能を追加することができる。RubyにたとえるとModuleをincludeさせるようなイメージ?

例として、次のようなTemplateInfo.plistを持つテンプレートを作成し、ユーザ定義のテンプレートの場所に配置する。

すると、このテンプレートは"Cocoa Application"をインジェクション先として持っていて、"Cocoa Application"のテンプレートの動作が変わる。実際にどうなるかというと、


  • "link QuartzCore framework"というチェックボックスがテンプレート選択後の画面に追加される
  • チェックをオンにすると、作成されたプロジェクトははじめからQuartzCoreにリンクした状態になっている

となる。"Cocoa Application"のテンプレートファイルはそのままで、ちょっと機能追加することができた。これがインジェクション。

Xcode 3以前ではテンプレートを一度まるごとコピーして、カスタマイズしたテンプレートを作成する必要があったけれど、Xcode 4では必要な部分のみを自作するという手法をとることができる

継承 (Ancestors)

インジェクションの他に、継承のようにテンプレートを作ることができる。これにはAncestorsキーで継承元となるテンプレートのIdentifierを指定することで利用できる。たとえば、テンプレート"Cocoa Application"では次のようになっている。

   // Cocoa Application.xctemplate/TemplateInfo.plist
   <key>Concrete</key>
   <true/>
     :
   <key>Ancestors</key>
   <array>
       <string>com.apple.dt.unit.cocoaApplicationBase</string>
       <string>com.apple.dt.unit.macReferenceCounting</string>
   </array>

ここでConcreteというキーがでてきたけれど、これがtrueのものが実際にXcodeの「新規プロジェクト」で選択できるテンプレートになる。Concreteがfalseまたは定義されていないものは、抽象クラスというか、他のテンプレートに継承されたり、他のテンプレートにインジェクションしたりするのが役割となる。

継承元のひとつめのcocoaApplicationBaseというIdentifierを持つテンプレートは"Cocoa Application Base"というテンプレートで、このテンプレートは"Cocoa Application"の他に"Cocoa-AppleScript Application"にも継承されている。

ファイル作成 (Definitions, Nodes)

プロジェクトにはプロジェクト自体の.xcodeprojファイルのほかにソースコードやリソースなど、さまざまなファイルがある。それらのファイルを定義するのがDefinitionsとNodesという2つのキーになる。

  • Definitions ファイル内容のパーツ。文字列または別ファイルへの参照。
  • Nodes 実際に生成するファイルおよびその内容の定義。

例をあげると

   // Cocoa Application Base.xctemplate/TemplateInfo.plist
   <key>Nodes</key>
   <array>
       <string>main.m:main:NSApplicationMain</string>
       <string>___PACKAGENAME___-Info.plist:Mac</string>
       <string>___PACKAGENAME___-Info.plist:ApplicationCategory</string>
       <string>___PACKAGENAME___-Info.plist:NSHumanReadableCopyright</string>
       <string>en.lproj/Credits.rtf</string>
   </array>

が、このテンプレートや継承元のテンプレートで定義されたDefinitionsに従って、

  • main.m
  • ...-Info.plist
  • en.lproj/Credits.rtf

のファイルを生成することになる。main.m:main:NSApplicationMainなどの展開がどのようにされるかはまだきっちり体系化できていないんだけど、右側からマッチしていって残ったのがパスになるような感じだったかと。

Definitionsによる定義は上書きができる。たとえば"Cocoa-AppleScript Application"では

   <key>Definitions</key>
   <dict>
       <key>main.m:import:importAppleScriptObjC</key>
       <string>#import &lt;AppleScriptObjC/AppleScriptObjC.h&gt;
   </string>
       <key>main.m:main:NSApplicationMain</key>
       <string>[[NSBundle mainBundle] loadAppleScriptObjectiveCScripts];
   return NSApplicationMain(argc, (const char **)argv);
   </string>

のように、"main.m:main:NSApplicationMain"キーの内容を上書きして、もともとのObjective-Cアプリケーションの単なるNSApplicationMain()の呼び出しの前に、AppleScript固有の初期処理を追加するようにしている。

まとめ

簡単にXcode 4のプロジェクトテンプレートの構成と機能について説明しました。

  • 別テンプレートでの定義をインジェクションできる
  • 継承ができる
  • plistからファイルを生成できる

という点がXcode 3以前との大きなちがいで、柔軟にテンプレートを構成できるようになっています。