Sandbox に悩まされることばかり

MacDev

6 月から Mac App Store のアプリケーションは Sandbox 化が必須になったけど、最近アプリケーションを作っていて悩むのはほとんどその Sandbox 関係だ。

PubSub.framework

RSS のフィードを扱いやすくしてくれる、Leopard で追加されたフレームワーク。今のところ Safari や Mail で使われている。

Lion になっても 1 つの PubSubAgent プロセスを複数アプリケーションで共有する仕組みが維持されていて Sandbox と相性が悪そうだ。と考えていたら本当に動かなかった。

しばらく調べて“Sandboxing the PubSub framework”という記事を発見。知りたいことがすべて書いてある。.entitlements ファイルの中身が temporary-exception だらけになってしまうのが悲しいけれども。

このフレームワーク、MobileMe のときは複数 Mac で未読フラグなどを同期してくれたのだけど、iCloud になってからは同期されなくなってしまったのが残念。最近は目立った改良もなく Apple もあまり力を入れていないようなので、この先どうなるのか将来性が不安。
それでもほとんどコードを書かずに RSS の取得/構文解析/登録を行うことができる優れものなのだ。

2013.12.8 追記:PubSub は Mavericks で見事に deprecated!自分が使わなくなったフレームワークは廃止する、それが Apple。

ログイン項目に登録 ヘルパーをログイン時に開く

多くのアプリケーションでは“ログイン時に開く”というチェックボックスがあってそれをクリックするだけでログイン項目の登録/削除ができるのだけど、Sandbox ではこれまでの LSSharedFileListInsertItemURL() を使った方法が使用できない。

解決策は Stack Overflow の“How to create a helper application for Mac App to start it on user login?”という質問に書いてある、SMLoginItemSetEnabled() を使った方法。
ただしアプリケーション自身を登録することはできず内包するデーモンのみ登録可能で、これまでのようにシステム環境設定の“ログイン項目”のところには表示されないという違いがある。

  1. Info.plist に LSUIElement を追加したヘルパーアプリケーションを用意。
  2. ヘルパーは親アプリケーションパッケージ内の Contents/Library/LoginItems/ に置くようにする。
  3. 親アプリケーション:LSRegisterURL() でヘルパーの URL を登録。
  4. 親アプリケーション:SMLoginItemSetEnabled() にヘルパーのバンドル ID を渡す。

この方法で開かれるのはヘルパーアプリケーションなので、これまでの代替にならない場合もある。「ヘルパーアプリケーションの中で親アプリケーションを開けばいいのでは?」と考えたが、Sandbox に弾かれてしまう。

アプリケーションを再度開く

自動アップデートの仕組みを提供する Sparkle で新バージョンをダウンロードしたあとにインストール・終了してから再度開く仕組みがあったけど、あれはアプリケーションを起動するためだけのプロセスを用意しておき、それを起動してからアプリケーションを終了することで実現している。

Sandbox を有効にすると、用意したプロセスの起動までは成功。だが“ログイン時に開く”の件と同様、そのプロセスから親アプリケーションを開くところで弾かれてしまう。

成功例を探したところ、LaunchAtLoginHelperというものを発見。ソースを読んでみると...

  1. 親アプリケーションの Info.plist で独自の URL スキームを定義しておく。
  2. ヘルパーでそれを使用した URL を開き、親アプリケーションを起動。

なんと URL スキームを使っていた。Apple が推奨しているのかは知らないけど iOS ではよく見かける。こんなところも Back to the Mac なのか...

UIKit for Mac はいつ?

MacDev

Retina ディスプレイが登場していろいろメリットはあるけど、そのうち重要な一つが、サブピクセルレンダリングなしでも文字が読みやすくなることだと思う。

“試用可能な場合は LCD で滑らかな文字を使用”というチェックボックスがシステム環境設定にあるけど、これがサブピクセルレンダリングに関する設定。

↑サブピクセルレンダリングによる違いとその説明を兼ねた図。通常のアンチエイリアス(上)では線の太さをグレーで表現していて、細く小さい文字だとピクセルが足りず擦れたようになってしまう部分があるけど、サブピクセルレンダリング(下)ではテキストカラーとは異なる色が少し混じっていて上記の原理でこの問題を解決している。

サブピクセルレンダリングには一つの大きな制約がある。それは、背景の色に依存していて必ず背景の上に描画しないといけないことだ。

AppKit の古くからの方式では 1 つのウインドウにつき 1 枚のグラフィックスコンテキストがあり、再描画する必要のある領域を親ビューから子ビューへと順番に描画して書き換えていく方式なのでサブピクセルレンダリングとは相性がいい。

iPhone では登場当時からサブピクセルレンダリングがないけど、基本的に Mac より文字のサイズ(ピクセル数)が大きくボールドが多用されていたこともありほとんど読みにくいことはなかった。iPhone 4 以降では Retina ディスプレイが採用され、通常のアンチエイリアスでも細い線までインクのように美しい。

iOS の UIKit は Core Animation の CALayer ベースでそれぞれのビューが独立したグラフィックスコンテキストを持ち、それらにあらかじめ描画しておいたものを GPU が移動や変形をするため滑らかなアニメーションが実現できるのだけど、これに加えてサブピクセルレンダリングを使用しようとすると

  • LCD 1 ドットの中で RGB の並ぶ方向に依存するため、ビューを描画する時点で画面に対する向きを知っている必要がある。もし画面が回転するようなことがあればすべての文字を再描画しなければならない。
  • 1 つのコンテキスト内で背景と文字を描画する必要があるため UILabel のようなビューではその背後のビューと同じ色の背景色を設定しなければいけなかったりして手間がかかる。
  • UILabel の背景にあるビューがグラデーションだったらどうする?

といったように、CALayer のメリットを打ち消してしまう。

なので UIKit のような CALayer ベースの UI Kit を Mac にも欲しいという声がときどきあるけど、そのメリットを最大限に活かすにはサブピクセルレンダリングを捨てなければならず、登場は Mac 全体に Retina ディスプレイが浸透してからではないかなと思う。

2012.6.17 追記

でも AppKit は NeXT 時代から続く歴史の長いフレームワークであるとはいえ、互換性を重視しすぎる他社のものと比べると数多くの脱皮を繰り返し、しっかりと時代に合った進化をしてきた部分も多い。

OS バージョンアップのたび徐々に CALayer のサポートが強化されたり、Lion でまったく新しい自動レイアウト機能が NSView に導入されたりするのを見ると、Apple もまだ本気で AppKit に力を入れているような感じもする。

Mac と AppKit はまだまだ長い付き合いかもしれない。

フイルタじゃない

MacDev

Mac Apps + Japanese に「母音がウの文字に続くイは大文字」なんて書いたけど、“フィルタ”は違うな。どう書けばいいんだろう。

Mac Apps + Japanese

MacDev

早速、DocumentsMac Apps + Japanese のページを追加。
これは以前から作ろうと思っていた、Mac アプリケーションの日本語化に関する文書だ。

ユーザは気にしなくてもいいけど、アプリケーションを提供する側に立つ人は細かい部分までこだわるべきだと思う。

(あまり気にしない開発者が多いのか、iOS アプリケーションの日本語表記はひどすぎるので自分は iOS デバイスをすべて英語環境にして使っている。)

ちょっとした疑問

MacDev

Dock.app は Dock のほかに Dashboard や Mission Control、Launchpad、スタック表示、デスクトップ背景など含んでいるのになんでこんなにメモリ消費が少ないんだろう。

まだ情報が足りない

MacDev

iOS SDK と一体化しているせいで Mac 開発者はしばらく触ることのできなかった Xcode 4.2。Objective-C 新機能の Automatic Reference Counting (ARC) が気になっていたのだ。
やっとリリースされたので、自作フレームワークを ARC に対応させるために調査中。

オブジェクトを retain しない辞書として使っていた NSMapTable だけど、Class Reference によると

Important: NSMapTable does not support weak references under Automatic Reference Counting (ARC).

ARC 環境での weak 参照はサポートされないとのこと。NSPointerArray や NSHashTable も同じらしい。これは strong 参照されてしまうという意味なのか、これまでのように、retain はしないけど解放時の自動 nil 化はされないので解放される前に取り除いてねという意味なのか、どちらなのだろう。
C 言語の id 配列を使っていた部分もあり、対策を考えないといけない。
各 Callback を NULL にした NSArray & NSDictionary はどうなんだろう。

Hint Balloons

MacDev

ソフトを初めて触るユーザに対して使い方を説明するために、初回起動時に小さなウインドウを表示して画像で示したりドキュメントを作って Web ページ上に置いてみたりと試行錯誤してきたけど、機能を見つけてもらえなかったり「使い方がわからない」「わかりにくい」という声は消えなかった。

そこで今回は各部分ごとに吹き出しを表示し、指示された内容を実際にユーザが試すと消えて次の吹き出しが現れるという、一種のチュートリアルのような方法を採用しようと考えている。ゲームなどでも見かけるやり方だ。