9月 | 2012 | HMDT Blog

カテゴリー : 2012年 9月

AppleにIn-App Purchase Contentをホストさせる


iOS 6のIn App Purchaseでは、ダウンロードするコンテンツをAppleのサーバにホストしてもらうことができる。それの実験中。

まずは、iTunes Connectに行くと、「Add Content」っていうボタンが増えている。なるほど。これでコンテンツファイルを追加できるのね。ってことで、Add Contentして、いくつかの設定をしたら、「パッケージファイルをアップロードしろ」って言われた。パッケージファイルってなに?

真っ先に思い浮かんだのは、Package Makerのこと。インストーラパッケージを作るためのアプリだ。最近使ってねーな。気がついたら、Package Makerはもう標準添付されていないし。Appleのサイトにいってダウンロードしないといかんのね。たぶんこれは違うだろ。

ドキュメントを探しまくってたら、「Xcodeを使って.pkgを作れ」って書いてあった。Xcodeで作れるのね。で、Xcodeのどこだ?

今度はXcodeの中をかけずり回って、やっと見つけました。[File]→[New]→[Target…]メニューを選択する。するとターゲットテンプレートの中に、「In-App Purchase Content」があったぜ。

これすか!これを作った後は、コンテンツファイルを追加して、アーカイブすると、XcodeからApp Storeにアップロードできる。とりあえず、コンテンツを送り込むところまでは成功したっぽい。

次は、アプリ側の変更だな。SKDownloadを使うようにしないといけない。これを使うと、待望のバックグラウンドダウンロードができるはずなんだよね。

iOS 6でEvent Kitでイベント追加には、追加のメソッド呼び出しが必要


Event Kitを使ってカレンダーにイベントを追加する場合、iOS 5までは特にユーザに同意を求めなくともできた。iOS 6からは、ユーザに同意を求める事が必須になる。

そのために、EKEventStoreにrequestAccessToEntityType:completion:というメソッドが追加された。これを呼ぶと、ユーザに同意を求めるアラートが、OS側から表示される。ここでOKをもらうと、カレンダーへのアクセスが可能になる。これを呼ばないiOS 5のソースコードでは、iOS 6でまったくカレンダーにアクセスできなくなるので注意。defaultCalendarForNewEventsでnilが返ってくる。

このメソッドは、アラートを表示するため、続きをcompletionブロックの中でやらないといけないので注意。さらに、completionブロックは、メインスレッド以外で実行されるようなので、さらに注意。iOS 5と共存するコードを書くには、ちょっと手を入れる必要あり。

Xcode 4.5がMac App Storeに登場、あとUIRefreshControl


iOS 6が、一般に公開。同時に、Xcode 4.5もMac App Storeに登場。これで、iOS 6ネタは解禁ということでいいのかな。

ということで、iOS 6ネタをひとつ。iOS 6からは、UI KitにUIRefreshControlが追加された。これは、いわゆる「引っ張って更新(Pull to Refresh)」を実現するコントロール。サードパーティアプリの機能を、本家が取り込んだ格好だ。

UIRefreshControlは、引っ張ると、うにょーんとのびる。この辺はAppleらしさが追加されたようだ。

UIRefreshControlの使い方は簡単で、インスタンスを作成して、スクロールビューにaddSubview:してやればよい。位置とか大きさとかは気にしなくとも、放り込んでおけば、あとは勝手に面倒を見てくれる。

UIRefreshControl* refreshControl;
refreshControl = [[UIRefreshConrol alloc] init];
[refreshControl addTarget:self action:@selector(refreshAction:) 
        forControlEvent:UIControlEventValueChanged];

[_scrollView addSubview:refreshControl];

スクロールビューであれば、UITableViewだろうが、UIWebViewのやつだろうが(scrollViewプロパティから取得する)、何でもオッケー。便利だ。

引っ張った後の更新は、アクションとして受け取る。UIControlEventValueChangedイベントが発行されるので、そいつを受け取ればいい。

iPhone 5からはarmv7sアーキテクチャ


iOS SDK 6がGMになったんで、過去のプロジェクトを再ビルドしまくっているけど、アーキテクチャにarmv7sが追加になったようだ。

ざっと説明しておくと、アーキテクチャはどのCPU命令セットに対してバイナリを作成するかを指定するもので、初代の頃のiPhoneはarmv6、その後、3GSあたりからarmv7になった。で、どうやらiPhone 5とiPod touch 5th generationからは、armv7sになるらしい。

Xcode 4.5を使うと、armv7とarmv7sのバイナリを作成できる。iPhone 5は、armv7sバイナリだと最高のパフォーマンスを発揮できる。armv7バイナリのみでも、動く。たぶん。でもarmv7sバイナリを用意するのが望ましい。

アーキテクチャの設定は、Xcodeのプロジェクトが、自前のソースコードしか使っていないのであれば、特に気にかける必要はない。でも、どっかからダウンロードしてきたり、他の会社から提供された.aファイルを使っている場合は、とても注意が必要。その.aファイルもarmv7sバイナリを含んでいないと、リンク時にエラーが出る。「file is universal but dose not contain an armv7s slice」って感じの。

そんなときは、アーキテクチャの設定からarmv7sを削除してやる。Xcodeプロジェクトの、ビルド設定で、「Valid Architectures」っていう項目があるので、そこからarmv7sを削除する。

こういうの、サイレントに追加してくるよな。CPUのコアはなんだっていいから、アーキテクチャの方を教えてくれ。

iPhone 5のディスプレイサイズ


その昔、最初のiPhoneが登場したときは、ガラケーのコンテンツ制作者に対して、「いやー、iPhoneアプリ作成は1モデルしかないから、楽勝っすよー」と、鼻高々だったわけですよ。iPhoneの新しいモデルがでても、画面解像度は同じだったから、「うんうん、分かってるよねー、Appleは」という感じだったんですよ。

iPadが出たときは、「うぐっ」となったけど、「まぁ、あれは別のデバイスだからね。1つのデバイスに凝り固まるのはよくないしぃ。これで、アプリの売上が倍になってくれれば…」といって対応したんですよ。ぜんぜん倍にならなかったけど。

Retinaディスプレイのときは、「ぐはぁっ」って感じだったけど、「さ、さすがApple!画像ファイル名に@2xを付けるだけでRetinaに対応してくれるなんて。わ、分かっているはずなんだよな?はぁはぁ」といってごまかしたんですよ。@2x付けるだけじゃ、ぜんぜん駄目だったけど。

そして、ついに来ました。もう、なんのいい訳もできないときが。iPhone 5の登場です。その解像度は、640×1136。なにそれ?!1136って、ねぇ、なあに!?いままでのものよりも、176ピクセル増えたって。176って何?

「ふ、解説しよう。176とはRetinaディスプレイでの値で、通常ディスプレイならば88ピクセル。これは、44ピクセルの2倍ということだ。44というのは、iPhoneの中では重要な意味を持つ値で、ユーザが指で操作可能な適切な大きさが、44ピクセルなのだ。ナビゲーションバーの高さなどはこの値になっている。その2倍の大きさだけ増やすとは、さすがApple、分かって…」

分かってねーよ!

ま16:9ってことなんだけど、ビデオ屋さんはいいにしても、整数値で割り切れてくれないとアプリ屋は困るんだよなー。

mergeChangesFromContextDidSaveNotification:って何のために?


先日は、MOSAでCore Dataセミナーをやってきました。参加された方々、お疲れさまでした。

で、そのとき質問があって、NSManagedObjectContextのmergeChangesFromContextDidSaveNotification:に関する事だったんだけど、そのとき「このメソッド、あんまりよく分からないんだよねー」と答えてしまいました。そしたら、アンケートで「このことが聞きたかったのに残念だ」という意味のことを書かれてしまったので、ちょっとこの場でフォローします。

まず、前提。状況としては、複数スレッドからCore Dataにアクセスしている。スレッド毎にmanaged object contextを作成し、同一のpersistent store coordinatorを使う。

まず、サブスレッドを立てて、managed objectを追加したとする。そうすると、そのスレッドのmanaged object contextでは、追加される。でもこの追加は、メインスレッドのmanaged object contextからは検知されない。どうもオブジェクトの追加は、managed object contextレベルで行われているらしい。サブスレッドが終了すると、この追加もそのまま消えてしまう。

そこで、サブスレッドでオブジェクトを追加した後、保存を行う。そうすると、追加されたオブジェクトがデータベースに保存される。データベースに保存されるので、永続化される。この変更は、メインスレッドのmanaged object contextからも知る事ができる。保存後にフェッチすれば、追加されたオブジェクトを取得できる。

で、保存すると、NSManagedObjectContextDidSaveNotificationが通知される。それを捕まえて、Appleのドキュメントによれば、managed object contextのmergeChangesFromContextDidSaveNotification:を呼んでやれば、変更がマージされる、ということになっている。でもねー、保存した時点でデータベースに変更が反映されているんだから、マージする必要あんの?ってのが疑問としてあった。だから、「Appleのドキュメントに、このメソッドを呼べって書いてあるから呼ぶけどさー、これ別に呼ばなくても構わないんじゃないの?なんのためにやるのか、よく分かんないんだよねー」って思ってた。

そのときの質問は、mergeChangesFromContextDidSaveNotification:はメインスレッドで呼ぶ事になっているけど、そのときに時間がかかったりしないか、パフォーマンスは大丈夫か、というものだったんだけど、基本的には重い処理は保存のところなので、それは別スレッドで呼ばれるから大丈夫だと思う。

この、mergeChangesFromContextDidSaveNotification:を呼んだときの、別managed object contextのオブジェクトのマージって、どんなことが行われるんだろか。いまメモリ上に保持しているmanaged objctに限ってマージされる、ってことなのかな。であれば、リフェッチすれば、そもそも気にしなくていいのかな。