UIDocumentInteractionControllerによる、アプリケーション間のファイル受渡し

昨年、iOSの中で、アプリケーション同士が連携するためのしくみ - The iPhone Development Playgroundで、UIDocumentInteractionControllerを使ったアプリケーション間連携について簡単に説明しました。そのときに少し触れたとおり、SysStats MonitorSysStats Liteの間で、UIDocumentInteractionControllerを使った連携機能を追加しましたので、もう少し技術的な説明をしたいと思います。

処理の流れ

以下に、全体の流れを示します。

ここでは、以下の2つの目的で、UIDocumentInteractionControllerを使った連携を使用しています。

(1) SysStats Liteから、SysStats Monitorへのプロセスブックマークの移行

SysStats Liteで蓄積したプロセスブックマークを、SysStats Monitorで流用するための機能です。

(1-1) Send ユーザの操作により、UIDocumentInteractionController経由で、SysStats Monitorが自動的に起動され、プロセスブックマークデータを送られる。
そして、SysStats Monitor側のInboxと呼ばれるフォルダに、そのデータが保存される。
(1-2) Import ユーザの操作により、Inboxからブックマークデータを読み込み、現在のブックマークデータにインポートする。
(2) SysStats Monitorのプロセスブックマークを、Email経由で共有

異なるデバイスのSysStats Monitor間でプロセスブックマークを共有するための機能です。
例えば、iPhoneiPadを所有しているユーザの場合、iPhoneで蓄積したプロセスブックマークを、iPad側に取り込む(Import)ことができます。

(2-1) Export ユーザの操作により、Message UI Frameworkを使って、現在のブックマークデータ添付ファイルとするメールを作成する。
(2-2) Send Emailとして送信する。
(2-3) Send 受信側のデバイスで、Mail.appでEmailを受信し、添付ファイルを開く操作をすることで、SysStats Monitorにプロセスブックマークデータを送ることができる。
(1-1)と同様に、SysStats Monitorが自動起動され、Inboxフォルダに、そのデータが保存される。
(2-4) Import ユーザの操作により、Inboxからブックマークデータを読み込み、現在のブックマークデータにインポートする。

プロセスブックマークデータのUTI宣言

Appleは、データタイプの識別子のための構文として、UTI(uniform type identifiers)を定義しています。
UIDocumentInteractionControllerに、データタイプを認識させるためには、そのデータのUTIを宣言する必要があります。

参考ドキュメント

UTIに関しては、Uniform Type Identifiers Overviewを参照してください。
Info.plistの各項目の設定値については、Information Property List Key Referenceを参照してください。

SysStats MonitorのInfo.plistに、以下のような設定を行っています。

	<key>UTExportedTypeDeclarations</key>
	<array>
		<dict>
			<key>UTTypeIdentifier</key>
			<string>[プロセスブックマークのUTI]</string>
			<key>UTTypeDescription</key>
			<string>SysStatsMonitor&apos;s proprietary processbookmark format</string>
			<key>UTTypeConformsTo</key>
			<array>
				<string>public.item</string>
			</array>
			<key>UTTypeTagSpecification</key>
			<dict>
				<key>public.filename-extension</key>
				<array>
					<string>[プロセスブックマークの拡張子]</string>
				</array>
				<key>public.mime-type</key>
				<string>[プロセスブックマークのMIMEタイプ]</string>
			</dict>
		</dict>
	</array>
UTTypeIdentifier プロセスブックマークのUTIを指定しています。
UTTypeTagSpecification プロセスブックマークの拡張子MIMEタイプを指定しています。
UTTypeConformsTo UTIは、適合性(Conformance)に基づく階層構造の中に定義します。
プロセスブックマークは、SysStats Monitor独自のデータフォーマットなので、public.itemという、最上位階層を指定しています。
テキストファイルであるからといって、public.textを指定した場合、次で説明するLSItemContentTypesにpublic.textが指定されているすべてのアプリで開くことが可能となってしまうので、注意が必要です。

SysStats Monitorがプロセスブックマークを受取るための設定

SysStats MonitorのInfo.plistに、以下のような設定を行っています。

	<key>CFBundleDocumentTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeName</key>
			<string>SysStatsMonitor&apos;s proprietary processbookmark format</string>
			<key>CFBundleTypeRole</key>
			<string>Viewer</string>
			<key>LSHandlerRank</key>
			<string>Owner</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>[プロセスブックマークのUTI]</string>
			</array>
		</dict>
	</array>

ここでのポイントは、LSItemContentTypesに、前述のUTTypeIdentifierにて指定した[プロセスブックマークのUTI]を指定しているところです。この設定によって、SysStats Monitorは、プロセスブックマークを受取ることができるようになります。

詳細は、Document Interaction Programming Topics for iOSの"Registering the File Types Your App Supports"を参照してください。

ドキュメントを送る側のコーディングイメージ

SysStats Liteの設定画面で、「SysStats Monitorへエクスポート」を選択すると、以下のような、ドキュメント送信先のアプリを選択するメニューが表示されます。

実際の処理は、UIDocumentInteractionControllerを使って以下のように記述されています。

    self.docController = [UIDocumentInteractionController interactionControllerWithURL:url];
    docController.delegate = self;
    BOOL isValid = [docController presentOpenInMenuFromRect:CGRectZero inView:self.tabBarController.view animated:YES];
    if (!isValid) {
        // 必要に応じてエラー処理(AlertPanel)
    }

メニューから、「SysStatsMon」が選択された後、UIDocumentInteractionControllerDelegateメソッドが呼び出されます。SysStats Liteでは、以下のように特に何もしていませんが、各タイミングで必要な処理があれば、それぞれに記述します。

#pragma mark UIDocumentInteractionControllerDelegate

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application {
    NSLog(@"!!!!! Sending to: %@", application);
}

- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
    NSLog(@"!!!!! Sent to: %@", application);
}

- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
    self.docController = nil;
}

ドキュメントを受取る側のコーディングイメージ

以下に、ドキュメントを受取る側である、SysStats Monitorのコーディングイメージを示します。

UIDocumentInteractionControllerDelegate経由で起動されると、application:didFinishLaunchingWithOptions:メソッドが呼ばれます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    if (launchOptions) {
        NSURL *launchedURL = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
        if ([launchedURL isFileURL]) {
            if ([[launchedURL pathExtension] isEqualToString:kProcessBookmarkFileExtention]) {
                
                // 確認メッセージのためのファイル名などの情報を取得
                NSString *fileName = [launchedURL lastPathComponent];
				...

				// AlertPanelを表示
                
            }
            
        }
        
    }
    return YES;
    
}

SysStats Monitorでは、プロセスブックマークを受け取った時に、以下のような確認メッセージを表示しています。

[補足] Inboxフォルダのパスは、以下のようにして取得できます。
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *inboxPath = [[paths objectAtIndex:0] stringByAppendingString:@"/Inbox"];

ドイツのpcwelt.deのビデオで紹介していただきました。

PC-Weltという、ドイツのパソコン雑誌のサイトで、SysStats Monitorをビデオで紹介していただいてます。

http://www.pcwelt.de/news/Systemmonitor-iPhone-App-SysStats-Monitor-im-Video-vorgestellt-1412442.html

ドイツ語ですが、映像から、このアプリの機能が的確に表現していただいているのを感じます。
Danke!

iTunes StoreとVirusBarrier X6は、相性が悪いのか??

ここ数日、iTunes Storeへの接続が調子悪く、まともに画面が表示されなくなって困っていました。もう一台のMacからは正しく表示されているので、このマシン固有の問題と考え、いろいろ調べてみたところ、以下の記事を見つけました。

Macウイルス対策としてVirusBarrier X6を使っているが最近Itune Storeへのアクセスやメールに対してフィッシングサイトとして認識してしまうようで警告が出るようになってしまった。後検出だと思うがアップデートを待つしかなのかな。

先日VirusBarrier X6がiTunes Storeをフィッシングサイトとして処理していたが新しいアップデートが適用されてから正しく処理するようになっている。修正されたのでしょう。

現在は解決されているとのことですが、私も自分のマシンにVirusBarrier X6をインストールしているので、何となく気になります。試しに、VirusBarrier X6のアンチフィッシング機能で、信頼するサイトに、itunes.apple.comを追加してみたところ、何とかトップ画面の表示はできるようになりましたが、やたらと遅いですし、ちゃんと表示できない画面もあります。
そこで、とりあえず、アンチフィッシング機能をOFFにしてみたところ、正しく画面が表示されるようになりましたが、それでは意味がありませんよね。
そんなとき、もう一つやり忘れたことに気づきました。NetUpdateを実行してみたら、フィルタのアップデートがありました!

早速適用してみたところ、問題はきれいに解決されました。きっと、何か不具合があったのでしょう。。
でも、今年の初め頃にも同じような問題が起きているので、何か相性が良くないところがあるのかもしれませんね。。

iOS4.2で、一部の標準アプリのプロセス名が変更されたようです


SysStats Monitor / Lite では稼働中のプロセス一覧を表示しています。実際のプロセス名と、ホーム画面に表示されている名前と一致している場合もありますが、それが異なっているものも多いのが実情です。
例えば、以下のアプリが該当します。

ホーム画面 プロセス名
Safari MobileSafari
iTunes MobileStore
メモ MobileNotes

このように、ただ単にプロセス名の一覧を表示するだけではわかりにくいので、SysStats Monitor / Liteでは、「プロセスブックマーク」という機能をつけて、ユーザがわかりやすい名前を付与して、その名前で表示できるようにしています。言い換えると、プロセス名の辞書のようなものです。ただ、すべてのプロセスに関してその作業を行うのはかなり面倒な作業となるので、いくつかの標準アプリについては、事前に登録済みになっています。

ところが、このプロセス名というのは、特に仕様として公開されているものではなく、あくまでも、システム内部で使用されている名称に過ぎません。予告なしに変更されることは、十分に予測されます。標準アプリだけでなく、みなさんが、自分でインストールした一般のアプリも同様です。
そういったことを織り込み済みで、このような方式を採用しています。

さて、本題に入りますが、iOS4.2からプロセス名が変更になったものがあることが判明しました。それも、機種ごとに異なる名前になっているようです。
以下に、iPhoneiPadの例を示します。


整理すると、以下の感じで、いずれもチルダ(~)のあとに、機種名が付加されています。*1

ホーム画面 プロセス名(4.1以前) プロセス名(iPhone4.2) プロセス名(iPad4.2)
連絡先 Contacts Contacts~iphone Contacts~ipad
マップ Maps Maps~iphone Maps~ipad
Game Center Game Center Game Center~ipho Game Center~ipad

その他、iPhoneでは、標準のカメラアプリのプロセスが独立したプロセスになったようです。
SysStats Monitor / Liteのユーザの方は、上記のアプリを起動し、ブックマークに登録しなおすことで、表示できるようになります。
次期バージョンでは、このあたりを考慮して、事前登録の内容を見直す予定です。

このように地味な機能ではありますが、意外に便利なので、ぜひご活用ください。

*1:きっと、iPod touchも同様だと思いますが、手元には、マルチタスキング非対応のtouch 2Gしかないので、別途調査したいと思います。

iOSの中で、アプリケーション同士が連携するためのしくみ

AppStore経由でリリースされているアプリは、Appleによって定められたガイドラインを守ることが義務付けられていることは、みなさん周知の事実です。マルチタスキング環境となったといっても、「バックグラウンドに常駐させて好きなように振舞う」ということは許されていません。開発者の方であれば、知っていることですが、バックグラウンド処理は、以下のような目的に限定して許可されています。

  • オーディオコンテンツの再生
  • 位置情報のトラッキング
  • VoIPアプリケーション(電話等)のインターネット接続維持

例えば、SysStats Monitorのようなアプリが、常駐プロセスとして動作し、メモリ使用状況を定期的に記録するなんてことはできません。
また、そもそも通信機能が制限されているので、アプリ間で通信してリアルタイムに連携することもできません。

以上、「できないこと」ばかりを挙げて、ネガティブな感じになってしまいましたが、マルチタスキング機能とは別な形で、アプリ同士が連携するためのしくみは、いくつか用意されています。それらも一定の制限はありますが、うまく活用すれば、いずれもアプリの使い勝手を向上することができます。ここでは、その中の代表的なものを3つ紹介します。
いずれも、多くの開発者の方はご存知ですし、ユーザのみなさんも、見たことがある機能だと思いますが、自分用のメモを兼ねて、概要を整理してみました。

URLによるアプリの起動

これは、初期の頃からサポートされている機能なので、開発者のほぼ全員がご存知だと思います。
ユーザであれば、アプリを使っているときに、「メールを送信する」という操作をしたときに、標準のメールアプリが起動されるという動作を経験したことがある方は多いと思います。そのときに内部で使用されているのが、この機能です。標準でサポートされているURLは、以下のドキュメントにて説明されています。

Apple URL Scheme Reference

一般のアプリも独自のURLスキームを定義して登録することで、他のアプリから起動されるということができるようになります。
特に用途がないので公開していなかったのですが、例えば、私がリリースしているアプリでは、以下のURLスキームを密かに登録しています。

SysStats Lite sysstatslite:
SysStats Monitor sysstatsmon:

これらのアプリを購入していただいている方は、上記のURLをiOSSafariのアドレスバーに入力して、「Go」をタップすると、それぞれアプリを起動することができます。
上記のアプリはサポートしていませんが、一般のURLと同様に、パラメタを渡すこともできますので、それを意識した実装をすれば、設定されたパラメタによって振る舞いを変えるといったこともできます。

技術的なことは、以下の記事がわかりやすく説明されているので、参考になるかと思います。
URL schemeを使ってアプリを起動する - 強火で進め

Pasteboardによるデータ共有

iOS3.0より、コピー/ペースト機能がサポートされていますが、それを実現しているのがPasteboard(UIPasteboardクラス)です。MacOSXiOSの中では、Pasteboardという呼称が使われていますが、一般的な「クリップボード」と同義です。
Pasteboardの最も多い使われ方は、テキストフィールド内でのコピペだと思いますが、実際には、画像やドキュメントなどのバイナリデータも格納することができます。さらに、どのアプリからも、参照/登録を行うことができるので、アプリ間で簡単にデータを共有することが可能となります。

Pasteboardには、標準で用意されているGeneral pasteboardと、各アプリが独自に作成するCustom pasteboardがあります。

General pasteboard

標準的な、コピー/ペースト操作で使用されるpasteboardですが、アプリからも任意のデータを格納することができます。

Custom pasteboard

アプリが任意の名称をつけて管理することができるpasteboardです。名称を知っているアプリだけがアクセスすることができます。

Pasteboardの大きな特徴として、データの格納は常に上書きであるということが挙げられます。General pasteboardは、予期しないタイミングで上書きされて自分が登録したデータが消えてしまう可能性があるので、アプリ間で意味を持った連携に使用するときは、Custom pasteboardを使うのが安全でしょう。
URL起動では渡せないデータがあるときに、Pasteboardは有効な手段になると思います。

UIDocumentInteractionControllerによるファイルの受渡し

上記2機能は、MacOSXでサポートされているものを、iOSに適用したものですが、UIDocumentInteractionControllerは、iOS独自のものだと思われます。
iOS3.2より追加された機能ですが、これも、すでに多くの方は見たことがあろうものです。
最も代表的な例は、標準のメールアプリとiBookの連携だと思うので、まずはそのときの操作の流れを解説します。
PDFが添付されているメールを受信し、添付ファイルを開くと、以下のようにドキュメントを参照することができます。

ここで、右上の「次の方法で開く...」をタップすると、PDFを開くためのアプリの一覧が表示されます。

その中から、iBookを選択することで、iBookが起動され、そのPDFファイルが表示されます。

このようなことができるようにするためには、各アプリは以下のようなことをする必要があります。

ファイル受取側のアプリ

「自分はPDFファイルを開けるよ」ということを、宣言する。

ファイルを渡す側のアプリ

UIDocumentInteractionControllerクラスを使って、ファイルを渡すための処理を実装する。

アプリが起動されるところはURL起動と似ていますが、ファイルを渡せるところが大きな進化ですね。ファイルだけでなく、オプションでパラメタを渡すこともできるので、さらにありがたいです。
以前は、URL起動とPasteboardを組合せないとできなかったことが、これだけで実現できてしまいますね。


以上、各連携機能の概要をまとめてみましたが、いかがでしょうか?

1本のアプリに多くの機能を詰め込み過ぎると、複雑化してしまい、使い勝手も保守性も悪くなってしまうことが考えられます。適度な単位でアプリを分割し、連携機能を活用するのも選択肢の一つではないかと思います。
特に、企業がiPhone/iPadを導入し、自社向けのアプリを作るような場合、1本のアプリに全機能を入れることはありえないので、このような連携機能は重要かもしれませんね。

現在、SysStats MonitorSysStats LiteUIDocumentInteractionControllerを使った機能追加を考えていますので、またそれが完成したときにもう少し技術的な説明をしてみたいと思います。
とりあえず、コーディングレベルでは、以下の記事がシンプルに解説してくれているので、参考になるかと。。
Cheat Code

The problem fixed version of SysStats Monitor has been released

Fixed a problem that are reported below, version 2.1.1, was released today.
<<Apology>>SysStats Monitor2.1 has an problem

I will be careful not to cause the same problem.

Thank you very much.