初期設定ファイルが初期化されて困る

ちょっと前からおかしかったんですよね。

Script Editor の環境設定でフォーマットを設定しても、いつの間にか初期設定に戻ってしまう。Xcode と Script Editor を使っているとそういうことがちょくちょく起きることがあった。

Script_Editor_Format_Prefs

なので、自分好みの設定をした初期設定ファイルを保管しておき、不具合があればファイルを差し替えるというスクリプトを使っていました。不便は不便だけど、すぐに自分の設定を再現できるので重宝していました。

ところが、初期設定を置き換えても設定が反映されないという不具合が起きるようになりました。

いつの頃からか初期設定の扱いが変わっていたのですね。知りませんでした。もう、てっきり Script Editor のバグだと思っていました。

OS X 10.8?それとも 10.9 辺りから初期設定がキャッシュされるようになり、設定ファイルの変更や削除を行ってもキャッシュされている設定で上書きしてしまうのですね。

セキュリティ?安全性のためなのでしょうが、アプリケーションを作成している時に初期設定をクリアしても新しい設定が反映されないのでちょっと迷惑。

設定のキャッシュは cfprefsd というプロセスが行っているようで、バックグラウンドでずっと起動しています。こいつが動いている限り、設定ファイルを置き換えても元に戻ってしまうようです。

好みの初期設定ファイルで置き換えるには次のような手順でできます。

  1. アプリケーションを一旦終了する
  2. 現在の初期設定ファイルを削除
  3. 保管している初期設定ファイルを ~/Library/Preferences に
  4. cfprefsd を kill する

cfprefsd は kill しても勝手に再起動します。これで自分好みの設定をアプリケーションに反映できます。これらの手順を AppleScript でまとめます。

このような感じですね。仕組みが分かれば、あとは応用ですね。defaults コマンドの初期設定一括削除は覚えておくと便利です。

初期設定を削除したり管理者権限を要求したりしているので、ご利用はご自身の責任で。

結局、Script Editor の初期設定が初期化されてしまうのはナゼだかわからないので根本的な解決になっていません。なにが原因か分からないんですよね...。

Xcode 6 でファイルテンプレートを自作する

Xcode 3 までは、AppleScriptObjC にもファイルテンプレートがあったんですよね...。

Xcode 3 ぐらいまでなら Xcode のカスタマイズの知識もあったのに、今ではその知識も通用しない。Xcode 6 の使い勝手を良くするために日夜努力をしています(Xcode 3 を久しぶりに起動したら日本語化されていてちょっと感動しました)。

では、 Xcode 6 にファイルテンプレートを追加するための方法を。

追加するのは AppleScriptObjC のテンプレートですが、方法や考え方は他の言語のテンプレートを作るときでも同じです。プログラミング言語用と限定すると使い勝手が悪いですが、書式の決まったテキストファイルの生成などにも応用できます。

Swift でも Objective-C でも ReadMe でも BSD ライセンスでも請求書でもなんでもござれ。

では、本題。テンプレートの作り方自体は、Xcode 4 の頃から変わりはありません。

Xcode に最初から入っているテンプレートは Xcode 内にあります。

/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/

一方、自分で追加するテンプレートは次の場所になります。

~/Library/Developer/Xcode/Templates/

テンプレートは 2 種類あります。プロジェクトテンプレートとファイルテンプレートです。取り上げるのはファイルテンプレートです。ファイルテンプレートは Templates に File Templates をいうフォルダを作り、そこにテンプレートを配置します。

~/Library/Developer/Xcode/Templates/File Templates/

さらに File Templates/ 以下にフォルダを作ると、Xcode の新規テンプレート作成ウィンドウでカテゴリとして表示されます。例えば、ASOC というフォルダを作ると Xcode でも ASOC というカテゴリが追加されます。

Xcode_File_Template_Categories

以上、テンプレートの保存場所と分類方法です。次にテンプレートの構成要素について。

Xcode のテンプレートは必要なファイルをまとめたバンドル形式になっています。最低でも必要なファイルは以下の通りです。

  • ___FILEBASENAME___.applescript
    • テンプレートの雛形。拡張子が applescript になっているけど、swift や m など、目的に応じたもので可
  • TemplateIcon.png
    • Xcode 上で表示されるテンプレートのアイコン(48 x 48)
  • TemplateInfo.plist
    • テンプレートの詳細を記した plist ファイル

これらのファイルを拡張子 xctemplate を持ったバンドル(実際はただのフォルダ)にまとめ、Templates/ に置くと Xcode でテンプレートとして認識されるようになります(テンプレートの雛形も plist ファイルも中身が空でもいい)。

ここまでで Templates/ は次のようになっています。

Templates_Directory

実際に Xcode で新規ファイルを作成すると、テンプレート選択画面に表示されます。テンプレートの構成要素は以上です。

最後に一番大事な TemplateInfo.plist について。このファイルに記述されている内容に従って Xcode はテンプレート選択画面にオプションを追加したり、テンプレートに記述されている変数を展開していきます。

テンプレートに記述されている変数というのは、アンダースコア 3 つで囲まれたアルファベット大文字の文字列のことです。主な変数は次の通り。

  • ___FILENAME___
    • ファイル名(拡張子を含む)
  • ___FILEBASENAMEASIDENTIFIER___
    • C 言語のスタイルに変換したファイル名(拡張子を含まない)
  • ___PROJECTNAME___
    • プロジェクト名
  • ___PROJECTNAMEASIDENTIFIER___
    • C 言語のスタイルに変換したプロジェクト名
  • ___USERNAME___
    • ユーザーアカウントの名前(短縮)
  • ___FULLUSERNAME___
    • ユーザーアカウントのフルネーム
  • ___ORGANIZATIONNAME___
    • プロジェクトで設定した組織名
  • ___DATE___
    • 今日の日付
  • ___TIME___
    • 現在時間
  • ___YEAR___
    • 年を表す 4 桁の数字
  • ___COPYRIGHT___
    • コピーライト文字列
  • ___FILEEXTENSION___
    • ファイル拡張子

では、実際にテンプレートファイルに上記の変数を追加してみましょう。___FILEBASENAME___.applescript というファイルをテキストエディタで作成し以下のように記述。

--
--  ___FILENAME___
--  ___PROJECTNAME___
--
--  Created by ___FULLUSERNAME___ on ___DATE___.
--___COPYRIGHT___
--

script ___FILEBASENAMEASIDENTIFIER___
    property parent : class "NSObject"
end script

ここ、重要なのですが、このテンプレートファイルはテキストエディタで作成してください。決して Script Editor で作ってはいけません。Script Editor で作成し、保存するとファイルのエンコーディングが us-ascii になります。このエンコーディングだと Xcode で利用できなくなります。

テンプレートファイルは UTF-8、改行コード LF で保存してください。

次に TemplateInfo.plist を編集します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>DefaultCompletionName</key>
    <string>Empty AppleScript File</string>
    <key>Description</key>
    <string>An empty AppleScript File</string>
    <key>Kind</key>
    <string>Xcode.IDEKit.TextSubstitutionFileTemplateKind</string>
    <key>MainTemplateFile</key>
    <string>___FILEBASENAME___.applescript</string>
    <key>Name</key>
    <string>Empty AppleScript File</string>
    <key>SortOrder</key>
    <integer>1</integer>
    <key>Summary</key>
    <string>An empty AppleScript File</string>
    <key>Platforms</key>
    <array>
        <string>com.apple.platform.macosx</string>
    </array>
</dict>
</plist>

それぞれのキーは適切な値で設定され、意味は以下の通りです。

  • DefaultCompletionName(文字列)
    • ファイル保存時のデフォルトのファイル名
  • Description(文字列)
    • テンプレートの説明
  • Kind(文字列)
    • テンプレートの種類。現在、Xcode.IDEKit.TextSubstitutionFileTemplateKind しかない
  • MainTemplateFile(文字列)
    • テンプレートとして利用されるファイル名(拡張子含む)
  • Name(文字列)
    • テンプレート選択画面に表示される名前
  • SortOrder(整数値)
    • テンプレート選択画面に表示される順番
  • Summary(文字列)
    • テンプレート選択画面に表示されるテンプレートの説明
  • Platforms(配列)
    • 利用される OS。com.apple.platform.macosx なら、OS X。com.apple.platform.iphoneos なら iOS。指定がない場合はテンプレート選択画面の iOS、OS X の両方に表示される

これらを記述し、Xcode で新規ファイルを選択すると次のように表示されます。

Template_Dialog_Keys

テンプレートを選択し、進んでいくと保存できます。

Save_Dialog

以上でシンプルなテンプレートなら作れると思います。次に Xcode でオプションを入力するファイルテンプレートの作り方。

Xcode で Cocoa Class といったファイルテンプレートを選択すると、次のような画面が表示されます。

Cocoa_Source_Template_Options

新規ファイルを作成するときのオプションですね。これらのオプションは TemplateInfo.plist に Options キーとして記述することで追加できます。

Options で利用される主なキーは次の通りです。

  • Default(文字列)
    • UI のデフォルト値
  • Description(文字列)
    • UI の説明(ツールチップとして表示される)
  • Identifier(文字列)
    • UI の識別子。この識別子を使ってテンプレート内の変数を展開する
  • Name(文字列)
    • UI のラベル
  • Required(真偽値)
    • UI が必要かどうか
  • Type(文字列)
    • UI のタイプ。statictextcombopopupclassbuildSetting 等がある
  • NotPersisted(真偽値)
    • 入力値が今後も利用されるかどうか
  • Values(配列)
    • Type で combopopupclass を指定した時に表示される値
  • RequiredOptions(配列)
    • 他の入力値に対応して使用するか否かを選択する

これら以外にもキーはありますが、割愛。

例えば、Options の Type に text と記述しておくとテキスト入力欄が追加されます。TemplateInfo.plist に以下を追加します。

<key>Options</key>
<array>
    <dict>
        <key>Description</key>
        <string>Description of input value</string>
        <key>Default</key>
        <string>MyClass</string>
        <key>Identifier</key>
        <string>productName</string>
        <key>Name</key>
        <string>Class:</string>
        <key>NotPersisted</key>
        <true/>
        <key>Required</key>
        <true/>
        <key>Type</key>
        <string>text</string>
    </dict>
</array>

保存し、Xcode で新規ファイルを選択し、進んでいくと次のような画面が表示されます。

ASOC_Custom_Template_Options

今までは最後のファイル保存でファイル名をつける必要がありましたが、今後はここで入力した値がファイル名として利用されるようになります。

もう少し複雑なオプションをつけてみます。現在は NSObject を継承した単純なスクリプトを作るだけですが、今度は NSView を継承したスクリプトも選べるようにします。この方法を理解するために Xcode で新規の Cocoa クラスを作ってみます。

Xcode で新規ファイルを選択し、Source の Cocoa Class を選び、次に進みます。これらのオプションにはそれぞれ Identifier が TemplateInfo.plist で設定されています。

Cocoa_Source_Template_Options_Flow

Xcode はここで入力された値をつなぎ、その文字列をもとにテンプレートを選びます。この例なら NSViewControllerXIBSwift にある ___FILEBASENAME___.swift と ___FILEBASENAME___.xib が選ばれます。

Cocoa_Template_Directory

オプションから利用されるテンプレートが入っているフォルダを探し、そのフォルダ内のテンプレートファイルを開く...このような感じですね。

では、実際にやってみましょう。

TemplateInfo.plist の Options を編集します。

<dict>
    <key>Default</key>
    <string>NSObject</string>
    <key>Description</key>
    <string>What class to subclass in the new file</string>
    <key>Identifier</key>
    <string>cocoaSubClass</string>
    <key>Name</key>
    <string>Subclass of:</string>
    <key>Required</key>
    <true/>
    <key>Type</key>
    <string>popup</string>
    <key>Values</key>
    <array>
        <string>NSObject</string>
        <string>NSView</string>
    </array>
</dict>

これでポップアップメニューから NSObject か NSView を選択できるようになります。NSObject を選択すると NSObject というフォルダ内にある ___FILEBASENAME___.applescript を使ってファイルを作成します。

ASOC_Custom_Templates

以上をまとめたものを GitHub で公開しています。

触れていない部分も多々あったりします。Options の Identifier を使ったテンプレートの変数展開とか Options の RequiredOptions キーのこととか...。

Options で Identifier に fileName などと設定します。テンプレートファイル内に ___VARIABLE_fileName___ と記述しておけば、入力された値に置き換えられます。

RequiredOptions キーは、あるオプションが選択されたら利用できるようになるオプションです。NSWindowController が選択されたら、XIB も同時に作るチェックボックスが利用可能になる...といった感じです。

これらのオプションを利用すれば、そこそこ凝ったテンプレートも作れるのではないでしょうか。駆け足でしたが、Xcode 6 でカスタムテンプレートを作る方法でした。