YosemiteではDropboxがFinderと深く連携して動作するようになりました。同期ステータスを示す✅バッジは専用の場所で描画されるようになり、Dropboxディレクトリ内のアイテムを選択した時のみ専用のコンテクストメニューも表示されるようになりました。
これは、Yosemiteで新たに追加されたフレームワークであるFinderSyncを利用して実現されています。このFinderSyncを利用することで、Dropboxっぽい見た目のアプリを作ることができるようになります。
FinderSyncでできること
FinderSyncの実体はApp Extensionです。FinderSyncを利用することで、特定のディレクトリに対して以下のことが実現できるようになります。
- ディレクトリ内のアイテムに対してバッジを設定する
- ディレクトリ内のアイテムが選択された時のコンテクストメニューを追加する
- Finderのツールバー項目を追加する
- ユーザがディレクトリを開いたことを検知する
FinderSyncは同期機能をもつアプリをFinder UI(と各種アプリのファイル操作パネル)と連携させるためのものであり、単体でファイルの同期機能を実現することはできません。これはコンテナアプリ側でFSEvents等のAPIを利用することで実現できるでしょう。
FinderSyncの使い方
XcodeでCocoaアプリのプロジェクトを開き、Finder Sync Extension
ターゲットを追加すると、FinderSyncの雛形が作成されます。FinderSyncフレームワークにはFIFinderSync
とFIFinderSyncController
の2つのクラスしかないため、容易に全体像を把握できると思います。FIFinderSync
のサブクラスを作成し、その中からFIFinderSyncController
のメソッドを呼び出す形で実装します。
以下に、実装例を示します。
class MyFinderSync: FIFinderSync {
let controller = FIFinderSyncController.defaultController()
override init() {
super.init()
// 対象のディレクトリを登録する
controller.directoryURLs = NSSet(object: NSURL(fileURLWithPath: "/Path/To/Mybox")!)
let doneImage: NSImage! = ... // ファイルのステータスに対応するバッジ画像の生成 ✅
controller.setBadgeImage(doneImage, label: "Done" , forBadgeIdentifier: "Done")
}
// アイテムが表示されるときに呼び出される
override func requestBadgeIdentifierForURL(url: NSURL!) {
let isDone = ... // ファイルの何らかのステータスを取得
if (isDone) {
// init内で用意したDoneバッジをアイテムにセットする
controller.setBadgeIdentifier("Done", forURL: url)
}
}
// 各種メニューを返す
override func menuForMenuKind(menuKind: FIMenuKind) -> NSMenu! {
var menu: NSMenu!
switch menuKind {
case .ContextualMenuForItems:
menu = ... // ディレクトリ内のアイテムを選択した際のコンテクストメニュー
case .ContextualMenuForContainer:
menu = ... // 対象ディレクトリ自体を選択した際のコンテクストメニュー
case .ContextualMenuForSidebar:
menu = ... // サイドバーにある対象ディレクトリを選択した際のコンテクストメニュー
case .ToolbarItemMenu:
menu = ... // ツールバーボタンをクリックした際に表示されるプルダウンメニュー
}
return menu
}
// ツールバーにボタンを追加する場合には、下記の3メソッドをオーバーライドする
override var toolbarItemName: String! {
return "Mybox Sync"
}
override var toolbarItemToolTip: String! {
return "Mybox Sync"
}
override var toolbarItemImage: NSImage! {
let toolbarIcon: NSImage! = NSImage(named: NSImageNameCaution)
return toolbarIcon
}
// 下記の2メソッドをオーバーライドすると、ユーザが対象のディレクトリ内に入るとき、出るときに通知される
override func beginObservingDirectoryAtURL(url: NSURL!) {
...
}
override func endObservingDirectoryAtURL(url: NSURL!) {
...
}
}
作成したFIFinderSync
のサブクラスをInfo.plistのNSExtensionPrincipalClass
キーの値として設定し、NSExtensionPointIdentifier
キーの値をcom.apple.FinderSync
とすることで、FinderSync Extensionとしてビルドすることができます。
まとめ
上記に示したような短いコードで、ハリボテではあるもののFinder上でDropboxのような見た目と挙動を実現することができ、開発者は同期機能などの実装に注力できるようになります。