Macでオリジナルプロトコルを実装してみた

# Macでオリジナルプロトコルを実装してみた

※ **2014/04/13** にシェルコマンドで空白を用いたい場合の処理を追記しました

私は、普段からコマンドラインランチャーとして、 **Alfred** というアプリケーションを使用しています。

Alfredでは、URLとコマンドキーワードを設定すれば、そのキーワードでURLを開く(たとえば、キーワード「google」でGoogle検索のURLを開く)ことができます。
私は、常々このAlfredでシェルコマンドを起動させることはできないかなーと考えていました。
(注:実はシェルコマンドの起動はAlfred ProもしくはAlfred 2を買えばビルトインで実装されていますが、私はお金払いたくないので実装しました) 

そして、Macのアプリケーションには、iTunesの「 **itunes:** 」のような **オリジナルプロトコル** が実装されているものがあります。
なので「 **shell:** 」というオリジナルプロトコルが実装されている **AlfredShellRunner** というアプリケーションを作って、
Alfredでシェルコマンドを起動できるようにしてやろう、と考えました。

( **要訳:** *Alfredというアプリでシェルコマンドを起動させたいからオリジナルプロトコルがついてるアプリケーションをつくるよ*)

> 参考: 
> OSXでオレオレ･プロトコルのヘルパアプリケーションを作成する - http://blog.katsuma.tv/2007/12/osx_protocol_helper.html

## アプリケーションの構成

Macでのアプリケーションは、 **.app** という拡張子がついたディレクトリによって表しています。

```
AlfredShellRunner.app
    - Contents/
        - Info.plist
        - MacOS/
        - Resources/
```

オリジナルプロトコルを実装するには、 **Info.plist** を使用します。

### Info.plist

Info.plistは、アプリケーションの設定情報(アプリケーション名やアイコンファイルパス等)を **XML** にしたものです。
オリジナルプロトコルの情報は、このInfo.plistに記載します。
その歳、「 **CFBundleURLTypes** 」というKEYタグを使います。

```
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>Alfred Shell Runner URL</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>shell</string>
        </array>
    </dict>
</array>
```

こんなかんじ。

**CFBundleURLTypes** には、これらのキー情報を設定出来ます。

+ CFBundleTypeRole **(必須)**  - string
+ CFBundleURLIconFile          - string
+ CFBundleURLName              - string
+ CFBundleURLSchemes           - array

#### CFBundleTypeRole

このキーは、URL タイプを尊重した、アプリケーションの役割を指定します。値は Editor、Shell、None のいずれかとすることができます。
(引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter3.html)

+ Editor(編集者)

    アプリケーションはそのタイプを読み、操作し、保存することができます。
    (引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter6.html#chapter6-2)

+ Shell(シェル)

    アプリケーションは他のプロセスのための実行時サービスを提供します―たとえば、Java アプレットビューアのように。書類の名前は（アプリケーションの名前の代わりに）ホストされるプロセスの名前です。そして、書類が開かれるたびに新しいプロセスが作成されます。
    (引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter6.html#chapter6-2)

+ None(なし)
    
    アプリケーションはデータを理解せず、タイプに関する情報の宣言だけを行います（たとえば、Finder はフォントのためのアイコンを宣言します【Finder はフォントを編集するためのアプリケーションではないが、ファイルアイコンを表示するためにフォントに関する宣言を行う、という場合】）。
    (引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter6.html#chapter6-2)


#### CFBundleURLIconFile

このキーは、この URL タイプに対して用いるための、アイコン画像ファイルの（拡張子を除く）名前を格納します。
(引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter3.html)

#### CFBundleURLName

このキーは、この URL タイプのための抽象化された名前を格納します。このキーは特定のタイプを参照するための主な手段となります。
また、この名前はタイプ名の「人が読める版」を提供するために、InfoPlist.strings ファイル内のキーとしても使用されます。
(引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter3.html)

#### CFBundleURLSchemes

このキーは文字列の配列を格納しており、そのそれぞれは、このタイプによって取り扱われる URL スキームを識別します。
(引用元: http://potting.syuriken.jp/potting_conv/BPRuntimeConfig_J/chapter3.html)

## アップルスクリプトを書く

AppleScriptエディタを開いて、以下の内容を入力。

```applescript
on open location shellURL
    if shellURL starts with "shell://" then
    -- "shell"プロトコルを開いた場合
        activate
        set len to the length of shellURL
        set shelltext to text from character 9 to character len of shellURL
        -- "shell://" 以降の文字列を取得
        
        tell application "Applications/Utilities/Terminal.app"
        -- Terminal.app を起動
            activate
            do script with command shelltext
            -- シェルスクリプトを実行
        end tell
        
    end if
    quit
end open location
```

### (追記2014/04/13) シェルコマンドに空白を用いたい場合

上記のapple scriptだと、 *shell://python -c 'print "hello"'* のようなシェルコマンドを使うことはできません。なぜなら、URLスキームでは空白が **%20** という文字に置換されるからです。

しかし、AppleScriptには文字列置換はできません。なので **delimitors** という機構を使います

```applescript
on replace(src, tg, rp)
	
	set oldDel to AppleScript's text item delimiters
	set AppleScript's text item delimiters to tg
	set myList to text items of src
	set AppleScript's text item delimiters to rp
	set myText to myList as string
	set AppleScript's text item delimiters to oldDel
	return myText
	
end replace
```

これで文字列を置換する関数が設定できました。
あとは、AppleScriptに文字列を置換する一行を書けば良いだけです。

```applescript

on replace(src, tg, rp)
	
	set oldDel to AppleScript's text item delimiters
	set AppleScript's text item delimiters to tg
	set myList to text items of src
	set AppleScript's text item delimiters to rp
	set myText to myList as string
	set AppleScript's text item delimiters to oldDel
	return myText
	
end replace

on open location shellURL
    if shellURL starts with "shell://" then
    -- "shell"プロトコルを開いた場合
        activate
        set len to the length of shellURL
        set shelltext to text from character 9 to character len of shellURL
        set shelltext to replace(body, "%20", " ")
        -- "shell://" 以降の文字列を取得して%20を置換
        
        tell application "Applications/Utilities/Terminal.app"
        -- Terminal.app を起動
            activate
            do script with command shelltext
            -- シェルスクリプトを実行
        end tell
        
    end if
    quit
end open location
```

入力したら、ファイル->保存で「 **ファイルフォーマット** 」を「 **アプリケーション** 」にして保存。

## Info.plistにプロトコル情報を追記

これでアプリケーションは完成したが、この状態では *まだオリジナルプロトコルの内容がInfo.plistに書かれていない状態* です。
なので、上記のオリジナルプロトコルの記述を、 **保存したアプリケーション/Contents/Info.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>CFBundleAllowMixedLocalizations</key>
	<true/>
    <key>CFBundleURLTypes</key>
    (省略)
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLName</key>
            <string>Alfred Shell Runner URL</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>shell</string>
            </array>
        </dict>
    </array>

</dict>
</plist>
```

追記すると上記のようになります。


## 保存したアプリケーションをdmgにする

しかし、追記しただけではまだダメです。この追記したInfo.plistを実行させるには、アプリケーションを一度 **.dmg** ファイルに圧縮して、インストールする必要があります。
DMGに圧縮する方法はこちらを参考にしてください。 -> http://memorva.jp/internet/mac/dmg_disk_image_file.php

アプリケーションをdmgにして、再度インストールして実行すると、もうオリジナルプロトコルが実装されています。
あとは、AlfredでカスタムURL検索で **shell://{query}** と入力すれば完了！！！