Tips of Create Alfred Workflow
何かしらの API を使ってインタラクティブに結果を返すかっこいい Alfred ワークフローを作るために最低限必要なことを学んだのでまとめます。
API キーを使うとき
API を叩くたびに API キーを入力するのは面倒なのでどこかに永続化したいです。
Keyword
オブジェクトで文字列を受取り、それを Write Text File
オブジェクトでファイルに書き込むことで永続化出来ます。
参照する際は各オブジェクトで $(cat /path/to/apikey)
するなり好きに参照すればよさげ。
重要なのは Write Text File
の設定項目でどのパスにファイルを置くか決めることです。選べるパスは workflow's data folder
と workflow folder
です。
workflow's data folder
は Workflow の設定で Bundle ID
が設定されている場合、 ~/Library/Application\ Support/Alfred\ 3/Workflow\ Data/<Bundle ID>
になります。
Bundle ID
が設定されていない場合は ~/Library/Application\ Support/Alfred\ 3/Workflow\ Data/
以下に直に置かれます。
workflow folder
はそのワークフローのカレントディレクトリです。 私は Dropbox で Alfred のデータ同期をしているので、その場合のパスは
~/Dropbox/Alfred/Alfred.alfredpreferences/workflows/user.workflow.4D089449-6AB9-47D4-8308-3C4F0F8E09D7
のような感じになります。
workflow folder
を指定して設置されたファイルは、 ワークフローをエクスポートする際に一緒に入ってしまいます。 うっかり API キーを入れたままエクスポートして配布なんてしてしまったら大変なので、基本的には workflow's data folder
を使ったほうが良いと思います。
あと、環境によって workflow's data folder
が何故かパーミッションエラーで読み込めない現象に遭遇しました。書き込みはできるのに読み込めない。謎。
その場合はファイルを書き込むパスを /tmp
などにすればとりあえず回避できます…。
また、変数を使ったデータの保持の例が公式のドキュメントに書いてありました。
Have you found yourself wanting to create a workflow where you need to enter or select multiple arguments to use later on in your workflow? Or wanted to make it easy for users to add their own identifier or API key to a workflow?
This is where you can use the new variables in Alfred 3; They can be used in a multitude of places - as keywords, arguments, in JSON utilities - and allow you to easily store an argument for use later in your workflow.
Using Variables in Workflows - Alfred Help and Support
でも変数だと毎回実行するたびに入力しなくてはならない気がするけど、どうなんだろう。
入力に対して結果を複数・動的に返したい
よくワークフローで、ユーザが入力した値をもとに検索をかけて動的にブワーってプルダウン表示するやつがありますが、あれは Script Filter
オブジェクトでやっています。
何かしらのスクリプトで決まった形式の JSON か XML を Script Filter
オブジェクトに渡すことで実現されています。
先日会社ブログでちょっと書きました。
上の例では、ただひとつの結果を返す JSON を返せば OK でした。
{
"items": [
{
"name": "たわし",
"price": 4000
}
]
}
しかし、複数の結果を返したい場合はどうすれば良いでしょう…。
{
"items": [
{
"name": "たわし",
"price": 4000
},
{
"name": "球根",
"price": 1000
},
{
"name": "土",
"price": 4500
},
{
"name": "フライパン",
"price": 100
}
]
}
Go でやろうとしてしばらく悩んでいたのですが、構造体のスライスがあることを知ったので、これを使って実現できました。
package main
import (
"encoding/json"
"fmt"
)
type Items struct {
Items []Item `json:"items"`
}
type Item struct {
Name string `json:"name"`
Price int `json:"price"`
}
func main() {
m := map[string]int{
"フライパン": 100,
"たわし": 4000,
"球根": 1000,
"土": 4500,
}
// items[] の中身を作る
var items []Item
for k, v := range m {
items = append(items, Item{Name: k, Price: v})
}
// items[] の中に入れて Marshal する
jsonBytes, _ := json.Marshal(Items{Items: items})
fmt.Println(string(jsonBytes))
}
Script Filter の結果毎にアイコンを変えたい
Script Filter の JSON フォーマットにはアイコンを設定する項目があります。
{
"items": [
{
"name": "たわし",
"price": 4000,
"icon" : {
"path": "./たわし.png"
}
}
]
}
なのでこれを使って結果に応じてアイコンのパスを切り変える処理を入れれば、結果毎にアイコンを変えることができます。
package main
import (
"encoding/json"
"fmt"
)
type Items struct {
Items []Item `json:"items"`
}
type Item struct {
Name string `json:"name"`
Price int `json:"price"`
Icon icon `json:"icon"`
}
type icon struct {
Path string `json:"path"`
}
func main() {
m := map[string]int{
"フライパン": 100,
"たわし": 4000,
"球根": 1000,
"土": 4500,
}
// items[] の中身を作る
var items []Item
for k, v := range m {
items = append(items, Item{Name: k, Price: v, Icon: icon{Path: "./" + k + ".png"}})
}
// items[] の中に入れて Marshal する
jsonBytes, _ := json.Marshal(Items{Items: items})
fmt.Println(string(jsonBytes))
}
Yay! 💫
開発中はスクリプトのシンボリックリンクを貼っておくと楽
そのまんまですが、毎回コンパイルなりスクリプト編集するなりして Alfred ワークフローのディレクトリに入れ直して… は面倒なので、開発中はシンボリックリンクを貼るか、 Alfred から直接開発環境のパスを指定すると楽。
公開するとき
ワークフローをダブルクリックするとワークフローの情報を編集できます。 Name
とか Description
とかはそのまんまですね。
Bundle ID
がなんぞこれって感じなので調べてみると、Bundle ID
を入れるとワークフローのバージョンを更新したときに検知できるようになるみたいです(どうやってるんだろう…)。
Bundle ID
の形式は特に決まっていないようですが、各個人の環境でユニークなものであるべきとのことです(同じ Bundle ID
のワークフローが存在すると Alfred がアップデート対象を識別できない)。形式としては com.<user name>.<workflow name>
がよく使われているみたいです。
ワークフローのバージョンはここから設定できます。
ワークフローのオブジェクトにはノートを追加することが出来るので、追加するとダウンロードした人が助かるかもしれないです。
この辺の、公開するためには〜的な話は以下にまとまっています。
ドキュメント
Alfred のドキュメントにオブジェクトごとの記事があるのでそれを見ながらやると良い感じ。
以下のリンクにオブジェクト( Inputs とか Triggers とか)毎でまとまっているのでここから辿っていくと良い。