Todayee シリーズ第4弾「Todayee Extension」をリリースしました。無料です。
iOSのシェア機能を利用してEvernoteへ投稿を行うアプリです。他のアプリで↑ボタンを押すと Todayeeアイコンが出てきます。
これをタップするとダイアログが表示され投稿することができます。
シェアボタン利用が中心なのでアプリ自体は設定画面のみ。
テキスト・URL・画像(1枚)が投稿可能です。カメラロールの写真はもちろん、SafariやRSSリーダー、メッセージなどで気になる情報があった時のURLの簡易クリップアプリとしても使えます。
投稿したテキストは左に線が表示される引用スタイルとなります。またURLは「リンク」という文字のリンクとなります。
以下、投稿例です。
Todayeeの特徴である複数アプリ・デバイスからの投稿を1つのノートへまとめる機能も、もちろん健在です。
他の Todayeeシリーズのアプリと一緒に使っていけます。
他の Todayeeアプリの記事
【アプリ】Todayee Text / Photo 1.0 をリリースしました
【アプリ】Todayee Silent 1.0 をリリースしました
開発メモ
ExetnsionからのEvernote投稿
初の Share Extension 開発は何箇所かでハマった。最初は Evernoteの投稿。Extensionを使う場面で認証させるわけにはいかないので、先に本体(Containing App)で認証した後にそれをExtension側で使うようにする必要がある。ただ本体アプリとExtensionは別々のアプリとして動作するのでそのままでは認証情報はもちろん、設定情報(NSUserDefaults)が両アプリ間で共有できない。幸い Evernoteの SDKには KeyChainグループを指定できるメソッド(ENSession.setSecurityApplicationGroupIdentifier)が用意されていたので、両アプリで共通のグループを設定することで認証情報を共有することができた。
Keychain Sharing
NSUserDefaults
NSUserDefaultsには NSUserDefaults(suiteName: name) というコンストラクタがあって、ここに両アプリで共通の文字列を入れておくと Containing App と Extension の間でデフォルト設定を共有することができるようになる。
なお前提として App Groups を1つ作っておいて、本体(Containing App)とExtension 両方で使えるようにしておく必要がある。
画像の NSExtensionItem
他のアプリ(Host App)から渡されるデータは NSExtensionContext を経由して取得することができる。面倒なのはデータの渡し方が元アプリによってまちまちなこと。例えば確認できているだけで画像の渡し方は3通りあった。
NSExtensionItem の実体:
・UIImage
・NSURL(file://で始まる画像の置き場所)
・NSData
これらはすべてデータのタイプが kUTTypeImageなので、実行時に NSExtensionItem の型を判定してそれぞれ処理してやる必要がある。アプリによっては UIImageを渡し、さらに kUTTypeURL で別に file://なNSURLを渡してくるものもあった(これはどうかと思うが)。
Extension で UIAlertController / ViewController
Extensionでは実行中に普通にUIAlertControllerが使える。Todayee Exntesionではエラーや注意メッセージを出すのに使っている。注意点はメインスレッドで呼び出すところ。
dispatch_async(dispatch_get_main_queue(), {
let controller = UIAlertController(title: ...
controller.addAction(UIAlertAction(title: ...
:
self.presentViewController(controller, animated: true, completion: nil)
})
同様に必要なら ViewControllerを作り画面を覆うような UIも作れる(同じくメインスレッドで)。Evernote投稿中に少し時間がかかりその間画面が固まってしまうので、このアプリでは ActivityIndicatorを表示するのに使っている。
起動後に説明画面
ネット上の情報で Extensionメインのアプリの場合、本体に操作方法の説明が無いとリジェクトされるとのことだったので、起動直後にヘルプのページを表示するようにした。
ビルドエラー
Extension にはいくつか制約があって、その中の一つに UIApplication の使用禁止がある。これが困るのは Extension でサードパーティのライブラリを使っていて、かつそれが UIApplicationを使っているケース。こんなエラーが出る。
このエラーを回避する為には、使っているライブラリのビルド設定で " Require Only App-Extension-Safe API" を YES → NO に変えてやれば大丈夫。
アップロードエラー
最後にハマったのが AppStoreへのアップロード。Xcodeのオーガナイザでいつものようにアップロードするとエラー。
リトライということなので、数日時間を開けてなんども実行。が、同じエラー。これはおかしいと思い調べたところ App Uploaderを使うと良いとの情報があったので使ってみた。ちなみに App Uploaderは Xcode7のメニューから呼び出す(知らんかった)。
App Loaderを使うともう少しマシなメッセージが出てきた。
調べると Extensionや Frameworkで CocoaPodsを使う時の既知の問題らしく、対処としては Extensionフォルダ内にできる Frameworksフォルダをビルドの最後に削除すること。※最後のWARNINGは Containing App と Extensionの Build番号(BundleVersion)が一致しない為のメッセージ。
そこで下記をExtension側の Build Phase の Run Scriptへ追記して実行したところ確かにエラーが減った。
が、まだ最後の1つが消えない。
スクリプトに ls > /tmp/ls.txt など追加して状況を調べるがディレクトリはあっていて Frameworksは存在する(ので、rm -frで削除されるはず)。原因は結局わからず時間ももったいないので、今回は自分で消すことにした。オーガナイザから作成した Archiveビルドをファインダで表示し、Extension配下の Frameworksを手で削除した。それをアップロードしたところ無事に完了。うまくいったので、まあこれでいいや。
あとがき
申請まで持っていくのに結構苦労させられたアプリだったが、申請自体は一発で通った。これはちょっとうれしい。今回もTodayeeプラットフォームがベースにあったのと、アプリの機能自体は少なかったので短期間で開発できたと思う。自分自身SafariやRSSリーダのURLを投稿するのに結構重宝している。
今後検討中のアプリ
・Place
・Pics
・Map
・Voice
・Viewer
: