8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OS XAdvent Calendar 2014

Day 4

FinderSyncでDropboxモドキを作る

Posted at

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フレームワークにはFIFinderSyncFIFinderSyncControllerの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のような見た目と挙動を実現することができ、開発者は同期機能などの実装に注力できるようになります。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?