Titanium MobileでLocal Notificationを実装する
iOSにはリモートからメッセージなどを通知するPush Notificationとインストール済みアプリから通知するLocal Notificationという二つの機構があります。以前、Push Notificationのサンプルはgistに公開したことがあるので、今回はLocal Notificationについて書いてみます。
Local Notificationが出来るのは
・ポップアップまたはステータスバーへの通知
・通知の際のサウンドの再生
・バッヂの操作(アプリのアイコンの数字のことね)
です。通知が発生した際にステータスバーまたはポップアップのOKをタッチしてアプリケーションを起動することができます。端末がスリープ状態のときはロック解除のところが下のalertActionで指定する文字になります。通知がユーザに無視された、またはユーザがキャンセルボタンをクリックした場合は何も起きません。また設定で通知をオフにした場合は何も表示されません。
*アプリを企画する方はここに注意してください。アプリを通知から必ず起動する方法はありません(やったらリジェクトされます)。
それから、通知を予約したアプリがフォアグランドで実行中には通知は実行されません。ただし、通知が発生していたことはアプリに通知されます。後ほど詳しくみていきます。
サウンドの再生ですが、どうやらシステムに組み込まれたサウンドを鳴らす方法はありません。サウンドデータを自前で用意することになります。対応しているフォーマットはcaf、aiff、mp3まで動作確認できました。wavとかもいけるかもしれませんが試していません。
サウンドの再生は、仕様では30秒までとなっていますが、29秒でないと動作しませんでした。またボリュームはプログラム側から制御することができません。端末でミュートしていたら音は出ません(じゃなきゃ困ります)。
繰り返しは毎日(daily)、毎週(weekly)、毎月(monthly)、毎年(yearly)から選択できます。何も指定しなければ一回だけ実行されます。繰り返しの場合は開始日時に過去の日付を指定すると次回からの実行になりますが、繰り返しがない場合は即時実行になってしまいますのでプログラム作成時に注意しましょう。
以上を踏まえて、作りたいアプリがLocal Notificationを使うのに相応しいかどうかを判断しましょう。
さて、実装です。Local Notificationではアプリいつ、どんなことをするかをiOS側のAPIに予約して、OKボタンの押下などの結果を受け取ります。
Ti.App.iOS.cancelAllLocalNotifications();
var notifications = [];
notification_params = {
alertBody: 'こんにちは、こんにちは',
alertAction: 'OK',
userInfo: {
data: {param1:'これはparam1', param2:'これはparam2'}
},
sound: 'sound.mp3',
repeat: 'daily',
date: new Date((new Date()).getTime() +(1000 * 10))
};
notifications.push(Ti.App.iOS.scheduleLocalNotification(notification_params));
まずcancelAllLocalNotificationsを実行して過去にこのアプリから登録したLocal Notificationを全てキャンセルにします。必要のないアプリであればここで実行する必要はありませんが、個別に通知を選んでキャンセルすることができないので、こうして一気にやってしまいます。
notificationsという配列を使っていますが、後で利用することはありません。が、アプリの終了後にすぐに消えてしまうと困るのでこうしていちいち宣言しています。
これでdateパラメータで指定された時刻(この場合は10秒後)に通知が実行されます。急いでアプリを閉じて待ちましょう。
実行されましたか?
あ、いまはまだこの記事を読んでいるだけで、実際に手を動かしてはいないんですね。わかりました。でも折角だからやってみましょう。今日は雨だし付き合いますよ。
実行されましたか?
よかったですね。ではもっと細かいところを見てみましょう。Local Notificationの予約はボタンを押せば実行されるようにしますか?でも、ボタンを押した瞬間にアプリを強制的に終了した場合はどうなるんでしょうか。変なタイミングで電話がかかってきてしまったら?Ti.App.iOS.scheduleLocalNotificationはvoid型で戻り値がないので、なんだか落ち着かない感じがしませんか?そうなんです、通常はLocal Notificationの予約はバックグランドで実行するのがベストプラクティスらしいので、ちょっと変更しましょう。値の受け渡しのデモもやります。
Ti.App.Properties.setString('message', 'こんにちは、こんにちは!');
Ti.App.Properties.setString('when','2012/04/23 00:15:00');
var service = Ti.App.iOS.registerBackgroundService({url:'service.js'});
service.jsはこちら:
if(Ti.App.Properties.hasProperty('when')){
Ti.App.iOS.cancelAllLocalNotifications();
var notifications = [];
notification_params = {
alertBody: Ti.App.Properties.getString('message'),
alertAction: 'OK',
userInfo: {
alertMessage:'ピンポンパンポン!'
},
sound: 'sound.mp3',
repeat: 'daily',
date: new Date(Ti.App.Properties.getString('when'))
};
notifications.push(Ti.App.iOS.scheduleLocalNotification(notification_params));
}
Ti.App.currentService.stop();
アプリがフォアグランドにある場合はLocal Notificationは実行されないといいましたが、実行するはずの時間になるとその旨だけがアプリに通知されます。これはnotificationというイベントとして捕まえることができます。userInfoを使って値の受け渡しまで実行できます。
Ti.App.iOS.addEventListener('notification', function(e) {
var dialog = Ti.UI.createAlertDialog({
title:'お知らせ',
message:e.userInfo.alertMessage,
buttonNames:['OK', 'お黙り'],
cancel:1
});
dialog.show();
});
こんなもんでしょうかね。Local Notificationのパラメータにはいくつか面白いものもあるのでぜひマニュアルをご覧ください。
Popularity: 2% [?]
Titanium MobileではAnalyticsを切っておきましょう
2年前の記事ですが、AppStoreのトップ無料や人気上位のアプリの68%がUDID(Universal Device Identifier)を送信しているという報告がありました(関連の日本語記事)。
利用状況などを収集してアプリの改善に利用するのはよくあることだとは思います。行動分析のためにデータを収集する場合、端末の識別のために何らかのIDが必要となるわけですが、スマートフォンでは最も簡単に利用できるのがこのUDIDです。このようなIDは以前から日本の携帯電話のサービスプロバイダでは広く利用されており、中にはウェブサービスの認証にまで使われているケースもありましたので、ある意味われわれにとっては非常に身近なものでもあります。
しかし、端末固有の一意なIDとして何かと便利に思われるUDIDですが、これを第三者に送信するのはあまり感心できません。上の報告ではHTTP通信のGETリクエストで送信しているアプリもあったようですが、これは論外です。しかし、もちろんそれだけが理由ではありません。
UDIDの送信がなぜ問題なのかについては諸説あるでしょうが、主な理由として考えられるのがエンドユーザの行動をトラッキングできてしまうことがプライバシーの侵害になるかもしれないという懸念です。同じようなことがいわれてきたブラウザのクッキーであれば、第三者のクッキー(Third-party Cookie)はブラウザの設定などでオプトアウトできるわけですが、UDIDは変更することができないため一度送信されてしまえば相手側の対応がなければ削除されません。アプリによってはその性格上、いつ起動していつ利用されたのか他人に知られたくないケースもあるでしょう。しかしUDIDが取得されてしまえば、第三者にその情報が漏れてしまいます。この手の試みはこれまでもウェブ広告の世界などでプライバシーとビジネスの間のせめぎ合いが何度も繰り返されてきた歴史があるわけですが、たいていはプライバシーの擁護が重視される結論に至っています。
ところでTitanium Mobileですが、Appcelerator社のサービスに登録している方なら一度くらいはAnalyticsの画面を見たことがあるかと思います。アプリのインストールされた数の推移や様々な行動分析が見られるので、開発者だけでなく企画等の担当者も大変重宝します。これらの情報はTitanium MobileのAnalyticsという機能を利用して送信されています。送信はSSLで暗号化されたPOSTリクエストになっているので、途中で覗き見られることについては、iPhoneの3G回線の通信がEnd-to-endのSSLではないなんてことはないでしょうから(ガラケーだとゲートウェイとウェブサーバ間のSSLになっていたりするようですが)、さほど憂慮する必要はなさそうです。さらに、当然ながらAppcelerator社は集積したデータをさらに第三者に転売したり、広告などに悪用したりするようなことはしないと明言しています。その点で現時点で懸念する必要はないと思います。しかし、いくつか問題はあります。(1)データを集積しているAppcelerator社はアプリの配信元、エンドユーザにとっては無関係の第三者であること(2)送信される情報についてアプリの利用者から明確な許諾を得ていないこと(3)UDIDが送信されていること、などがそうです。
これらの対応策は非常に簡単で、tieapp.xmlをテキストエディタなどで開いて
<analytics>false</analytics>
とするだけでこの機能をオフにすることができます。個人的な意見ですが、上記の理由から、現時点ではリリースされるアプリについては必ずこれをやっておく方がよいでしょう。もちろん、UDIDの送信が即、何か悪いことをしている証拠にはならないと思います。しかし、このような取り扱いに注意が必要なデータについては、トラブルを避けるためにも万全な状況でなければ利用しない方がいいはずです。また、iOS5からUDIDの取得が非推奨となったことに伴い、やはりUDIDの送信は避けるべきでしょう。
実際のところどうなっているのか確認してみましたが、残念なことに1.8系の実装(iphone/Classes/PlatformModule.mやiphone/Classes/TiUtils.mを参照のこと)を見る限り、Titanium.Platform.idを送信していますね。1.8.0.1の場合はiphone/Classes/AnalyticsModule.mmを見るとわかります。実際にデータをダンプするには(SSLの通信をキャプチャするような面倒なことが好きな人を除いて)このファイルを下のように変更します。
$ git diff iphone/Classes/AnalyticsModule.mm
diff --git a/iphone/Classes/AnalyticsModule.mm b/iphone/Classes/AnalyticsModule.mm
index f76a472..46c6e81 100644
--- a/iphone/Classes/AnalyticsModule.mm
+++ b/iphone/Classes/AnalyticsModule.mm
@@ -217,6 +217,9 @@ NSString * const TI_DB_VERSION = @"1";
[request setUseCookiePersistence:YES];
[request setShouldRedirect:YES];
NSString * stringifiedData = [SBJSON stringify:data];
+ //debug
+ NSLog(@"[DEBUG] Analytics sending data: %@", stringifiedData);
+ //debug
[request appendPostData:[stringifiedData dataUsingEncoding:NSUTF8StringEncoding]];
[request setDelegate:self];
ご覧頂ければわかる通り、UDIDが送信されていますので、やはり上に書いた方法でAnalyticsは停止した方がいいですね。それと、Appcelerator社が悪意をもってこの機能を実装しているのではないこともおわかり頂けるかと思います。だって、そうじゃなきゃソース公開してないですよね?個人的にはこういうところがTitanium Mobileを使い続けることの大きな理由になっています。
*決してここの部分をいたずらして集積サーバに変なデータを送り込んでしまおうとか考えてるわけじゃないですからね!
また、この点について各方面に問い合わせたところ、Analytics自体は非常に有望なサービスなので、今後はUDIDを利用しないよう変更する予定になっているとのことでした。そのときが来たら再度検討してみたいと思います。
この数日、エントリを書いておきながら公開するべきか非常に悩んでいました。上で説明はしたつもりですが、これは別に情報が勝手に第三者に集積されてしまったというような事件ではなく、単純に機能としてソースまで公開され、ドキュメントにも書かれていることなわけで、簡単に調査も可能なことですから、開発者としては知っていてある意味当然なことです。ただ、どこかで誰かがこの機能について悪意的にかき立てるようなことがあれば、まだまだ少数のコミュニティで活発にやり取りしている状態のTitanium Mobileが今後プラットフォームとして成長する妨げになってしまうような打撃を受けると嫌だな、と思ったので、情報の周知徹底を目的に公開することにしました。
もう一度まとめます。
(1)Analyticsはオフにできるよ
(2)オフにしとけばなんにも怖くないよ
(3)UDIDは今後は使われなくなるよ
以上です。
Popularity: 3% [?]
Titanium Mobileでアプリ内課金(iOS編)
アプリ内課金の実装方法について知りたいという声があったので、参考になればと書いておきます。アプリ開発者の方は権利の15%で取引に応じますの苦労がこれで少しでも減るなら喜ばしいことです。とはいえ、世知辛い世の中ですから、このコードで生じた金銭的問題の責任は放棄しますので、ご利用の際はあくまでも参考程度にとどめ、十分検証した上で実装されるようお願いします。
一応、動作はTiStorekit1.4、Titanium Mobile SDK 1.8.1、非消費型コンテンツで確認しています。
iOSの場合、課金の手続きは
- In App Purchaseが有効になっているかを確認する
- 正しいProduct IDかを確認する
- 購入開始
- 成功ならレシートを受信してチェック、キャンセルやエラーならエラー処理
となります。ここではAppcelerator社から公開されているTiStorekitを使った実装の例で説明します。以前に実装したものを参考にざっと書いた関係で、細かい条件は検証していませんのであしからず。CoffeeScriptだけどいいよね?
事前にiTunes ConnectでIn App Purchaseの設定が完了していることが前提になります。iTunes Connectの使い方は割愛しますが、支払い関連の設定が完了していないとIn App Purchaseは有効になりません。またProvisioning Portalで設定を確認して、正しいProvisioning Profileでビルドしましょう。iOS5からシミュレータからも利用できるようになったようですが、必ず実機で動作確認してください。
そうそう、下のコードではコンストラクタで@SANDBOXをtrueにしていますが、リリース時には変更しましょう!
class InAppPurchase
constructor:()->
@ERROR_PRODUCT_ID = {type:'product_id', message:L('inapp_error_product_id')}
@ERROR_CANMAKEPAYMENT = {type:'canmakepayment', message:L('inapp_error_settings')}
@ERROR_UNKNOWN = {type:'unknown', message:L('inapp_error_unknown')}
@SANDBOX = true
start:(data, success, error)->
###
args dataは課金対象の商品オブジェクト({product_id:プロダクトID})
sccess、errorはそれぞれcallback関数
###
storekit = require 'ti.storekit'
if data.product_id == '' || data.product_id == null || data.product_id == undefined
# なんて防衛的なコード! #
error(@ERROR_PRODUCT_ID)
else if !storekit.canMakePayments
# AppStoreが有効になっていない #
error(@ERROR_CANMAKEPAYMENT)
else
# 正しいProduct IDかどうかチェック #
storekit.requestProducts([data.product_id], (e)=>
if e.success
product = e.products[0]
if product != null
_success = success
_error = error
storekit.purchase(product, (elem)->
if elem.state == storekit.FAILED
error(@ERROR_PRODUCT_ID)
else if elem.state == storekit.PURCHASED
callback = (response)=>
if response.success && response.valid
_success()
else
_error(@ERROR_UNKNOWN)
# subscriptionの場合は下のオブジェクトにsharedSecretプロパティを追加 #
args = {
receipt:elem.receipt
sandbox:@SANDBOX
callback:callback
}
storekit.verifyReceipt(args)
else if elem.state == storekit.RESTORED
storekit.restoreCompletedTransactions()
success()
else
Ti.API.info "purchasing..."
)
else
error(@ERROR_UNKNOWN)
else
error(@ERROR_PRODUCT_ID)
)
TiStorekit = new InAppPurchase()
TiStorekit.start(product, success, error)
canMakePaymentsは定数でiOSの設定画面でAppStoreが有効になっていないのでそもそもアプリ内で課金ができない場合はfalseになります。requestProductsでProduct IDが正しいかどうかを確認し、purchaseで購入処理を開始します。successが返ればveryfyReceiptで確認して完了処理を実行します。
レシートの確認まで実装しましたが、ここは迷うところですね。通信に時間がかかったり、またはこのタイミングで処理が中断してしまった場合、利用者にとってはお金を払ったのに購入が正常に完了しないように見えてしまいます。時間がかかるようならキューに入れてレシートは後で確認し、いったん完了処理をしてから、不正があったらそれなりの処理をする、といったような手段で対応するしかないでしょう。かといってレシートを確認しないと、不正な利用を完全に防ぐことができなくなります。
Popularity: 4% [?]
どうしてTitanium Mobileなの?
ウェブ開発者がiOSやAndroid向けアプリを作ろうと思い立った場合、今なら大きく分けて2つの選択肢があります。ひとつは他の人たちと同じようにObjective-CやJavaで素直にアプリを作ることです。まあ、当たり前ですね。もうひとつの方は、PhoneGapやTitanium Mobileのようなサードパーティーのツールを利用してJavascriptなどウェブ開発者の慣れ親しんだ言語で開発するやり方です。それぞれ一長一短があります。先のやり方では、ネイティブな開発用言語だけあって全ての機能を最大限に活かすことができます。AppleやGoogleも最大限の開発支援を提供してくれることでしょう。しかし、Objective-CやJavaに慣れ親しんでいた人でなければ学習曲線がなかなか上昇してくれないかもしれません。特にウェブ開発者はWebObjectsをやっていた人でもない限りはObjective-Cに馴染みの無い人が多いでしょう。また、ヘッダファイルを書くことやDelegateみたいなまだるっこしいデザインに疲れてしまうかもしれません。そんな場合、サードパーティーのツールで素早く開発しながら徐々に慣れていく方が安全なやり方といえます。一方で、サードパーティーのツールには最上位のベンダつまりAppleやGoogleの意向でいつ排除されてしまうかわからないというリスクもあります。これまで見たところでは、今年や来年にそんなことが起きるとは到底考えられないのですが、それでも細かいトラブルはあるでしょう。
以上のような状況を鑑みるに、もしあなたがObjective-CやJavaでの開発に不安が残る状態であるなら、迷わず後者の方を選択するべきでしょう。その上で、自分の開発しているプラットフォームについて知ることは重要であり、そうであればいずれはネイティブな言語での理解が必須になるでしょうから、得られる知見を最大限に活かしてObjective-CやJavaでの開発について学んでいくのが理想です。例えば、PHPを使って開発をしていても、それだけしか知ろうとせず、ウェブサーバやPHP自体の実装といった抽象化されて隠された部分に分け入って進もうとしない人が、筆者の経験ではありますが、競争に耐えうる優秀な人材であった試しはありません。幸いにもサードパーティーのツールにはソースコードにアクセスできるものもあるので、良い手本になることがたくさん見つかるでしょう。
では、数あるサードパーティーのツールの中で、一体どれを選べばいいのでしょうか。
もちろん、おみくじで決めたとしても、それは当人の責任の範疇ですから、全く構いません。しかし、それでもやはり、いくらなんでももう少し真面目に考えないと不安になるかもしれません。なので、そうですね、それぞれのツールの特徴を掴んだ上で判断する方がいいでしょう。私自身も決定的な答えを用意しているわけではないので、知っている範囲で自分の意見を述べたいと思います。情報は最新とは限りませんので、ここは違う、いや今はこうなっている、などなどご意見あれば是非聞かせてください。いずれにせよ、現段階ではちょっとした宗派の争いみたいになりがちな話題なので、リラックスしてお読みくださいね。
サードパーティーのツール、と上ではひとくくりにしましたが、大きく分けるとこれも二種類に分かれます。ひとつはPhoneGapやRhodesのようにHTMLとJavaScriptで作ったUIをWebView(UIWebView)を使って表示させる、ウェブアプリとネイティブアプリが合体したようなもの、もうひとつはTitanium MobileのようにネイティブなAPIを叩いてJavascriptで記述しながらネイティブアプリとして動作するものです。ここでは実際に使ったことのあるPhoneGapとTitanium Mobileを比較します。
私見では、PhoneGapのいいところは第一にそれがシンプルであることです。例えば生成されるJavaのクラスは非常にシンプルで、実際に使ってみたところカスタマイズも容易でした。APIも数えるほどしかなく、プラグインの作成もそれほど難しいものではありません。また、UIはHTMLとJavascriptで作るので、ウェブ開発者やデザイナのこれまでに培った技術を活用することができそうです。
それに、例えばFacebookアプリは現在PC向けのものはHTMLで作られていますが、モバイル向けも今後は同じくHTML5を利用して記述され、Facebookのモバイル用アプリ内で動作するといったものになっていくことが予想されます。クロスプラットフォームでの開発が容易なのはHTMLの強みですから、こうした流れにソーシャルゲームのプラットフォームも追随していくのではないかと思います。
しかし、欠点もないわけではありません。jQuery MobileやSencha Touchといったツールを使ってなるべくネイティブアプリに似せることは出来ますが、それでもUIはネイティブなものではありません。これらのツールキットはまだ開発されてから日も浅くベストプラクティスも少ないので、開発はお世辞にも快適とはいえないのですが、それなのに特にiOSではHTMLとCSSででっち上げた「ネイティブっぽい」UIの「偽物」っぽさが際立ちます。動作もまだまだ快適とはいえません。またネイティブな機能への橋渡しをしてくれるAPIも豊富とは言い難いので、少しでも複雑なことに挑戦すると結局Objective-CやJavaでプログラムを作成する必要が生じます。UIレベルの話ならまだしも、例えばiCloudへの対応といった要求にはツール側での対応を待つかそれなりの工数を費やしてプラグインを作成することになってしまうでしょう。それに、これは年月が解消してくれる問題なのかもしれませんが、例えばSnapdoragonの第1世代のIS03のような貧弱なハードウェアではWebViewのようなリソースを消費するものはどうしても動作が遅くなります。オリエンテーションの変更の際にレイアウトが酷く崩れるといったユーザに不安を与えるような現象が発生することもあります。
最近、PhoneGapの開発元がAdobeに買収されることが発表されましたが、資金的には大きな後ろ盾を得たと同時に、大企業であり、これまで数々のプロダクトをディスコンにしてきたAdobeにその命運を握られてしまったともいえます。
以上、駆け足ですが個人的に思うところを述べてみました。先ほども書きました通り、ここは違う、いや現在はこうなっているといったご意見などございましたらお知らせください。
さて、その一方でTitanium Mobileの場合、Javascriptで記述した内容は評価された後にプロキシを通じてiOSやAndroidのAPIに渡されて実行されるため、実際に動作するのは通常のアプリと同じものなので外観は全くネイティブなアプリと変わりません。と同時に、Titanium.UI.WebViewというものが用意されているので、やろうと思えばHTMLとJSでUIを作ってしまうことも可能です。またこのJSとネイティブな機能を繋ぐのも非常に容易なので、この点でも大きなアドバンテージがあります。動作速度はネイティブアプリと比較すれば、インタプリタとコンパイル言語ほどではなくても、まあある程度の差はあります。しかし、シビアな反応が求められるゲームや電子書籍リーダといった特定用途のアプリでなければ実際に問題になるほどではありません。ネットワーク越しにデータをやり取りするアプリ、ウェブサービスのフロントエンド用アプリとして利用するには十分といえます。それに、驚くべきことに、最近ではQuickTiGame2dのような高速で動作するゲームエンジンも開発されていますので、ゲームの開発に利用されるようになる日も近いでしょう。また、このようなサードパーティーのモジュール開発も盛んで、Open Mobile Marketplaceで有料、無料を問わずたくさんの機能拡張用モジュールが公開されています。Githubにソースコードが公開されているものも少なくありません。
それから、jQuery MobileやSencha Touch(Ext.JS)といった一種独特なJavascriptよりも、commonJSといったモダンなJavascriptで記述することが推奨されているのもポイントです。最近ではNode.jsのようなサーバサイドのJavascriptもありますが、Titanium MobileのJavascriptもCoffeeScriptやcommonJSのような今時の技術を習得する近道となるでしょう。というのも、Titanium Mobileは基本的にはよくあるイベント駆動型のGUIのプログラミングなので、Node.jsのようなコールバックにつぐコールバックの連続にも面食らうことはありません。
と、いいことばかり並べましたが、もちろん問題もないわけではありません。Aptanaを買収してEclipseベースの独自のIDEがリリースされましたが、Eclipseがそうであるように人によってはあまり快適とはいえないものです。もちろん使わないで開発することも可能ですが、最初はやはり公式なツールを使いたくなる方も多いでしょうから、そこら辺で面食らうこともあるかもしれません。またビルドスクリプト内部にビルドにかかる時間短縮のため高速化する工夫があるのですが、何度も繰り返してビルドするうちにそれが邪魔をしてアプリの挙動がおかしくなることがありますので、ときどきCleanしてあげる必要があるといった癖のあるところも見受けられます。また海外製品にありがちなことですが、Pythonを使ったビルドスクリプトはUTF-8のMac OS Xで日本語の処理に問題がある箇所が多数ありますのでアプリ名称やプロジェクト名、ソースコードのフルパスに日本語が含まれていると辛い目に遭います。その辺りは、日本語でも有志によるサポートコミュニティがありますので、活用してください。
これらは今後改善されるべきことではありますが、一方で大半は公開されたソースコードから追いかけることも可能なことであり、ついでにいえばパッチを送って直してしまうことだって出来ます。個人的には、いつか大金持ちになって暇を持て余すようになったら、面倒の多いPythonの部分を全部Rubyにしてしまいたいと思っています。また、Titanium Mobileを入り口としていろいろと学びましたが、逆にネイティブな言語で開発しなくてもいいやという気にもなってきました。やっぱりDelegateとかってうんざりますよね?
以上、駆け足で比較していきましたので粗雑な部分、間違いなどもあるかもしれません。上にも何回か申し上げましたように、これは違うぞ、といったところがあればどしどしご指摘ください。よろしくお願いします。
Popularity: 7% [?]
Titaniumでリジェクト・iCloud関連
Titanium Mobileで作ったアプリがリジェクトされたので記念に。
今回リジェクトされた理由は、iCloud対応でデータの格納場所が厳密に定められ、チェック内容が変更になったからです。これからのアプリは生成されたデータを格納する際には以下のルールに従う必要があります。
(1) アプリが再生成できないファイルは/Documentsに保存
例えば、メモ帳アプリでユーザが書いたメモなんかはこちらに保存しないといけません。iCloudで自動的にバックアップされます。
(2) 再生成可能なファイルは/Library/Cachesに
データベースのキャッシュも含む、とありますが、ダウンロードされたファイルなどもここに置くのが正しいようです。
(3) 一時的なファイルはちゃんと/tmpに
アプリ終了時に消すのをお忘れなく。
(4) バックアップされては困るデータは”do not back up”
オフラインでの利用などで使いたい、iCloudやiTunesにバックアップされては困るファイルにはこの属性を追加しましょう。
さて、Titanium Mobileですが、例えばsqliteを使った場合、データファイルは/Library/Application Support/database以下に保存されてしまいます。残念ながら、Appleの新しい規制ではこれはリジェクトの理由になります。
Ti.Database.install('Resouces以下に置いたdbファイル', 'my_app');
このようにinstallを使って設置したデータファイルは/Library/Application Support/database/my_app.sqlとして保存されます。
これをAppleの規制に適応されるためには、例えば
Ti.Database.install('Resouces以下に置いたdbファイル', '../../Caches/my_app');
とすることで、/Library/Caches/my_app.sqlとして保存することができるようになります。
また、ファイルを保存する際にTi.Filesystem.applicationDataDirectoryを使っていた場合は
var root_dir = Ti.Filesystem.applicationDataDirectory + '../Library/Caches/';
このように変更すると/Library/Cachesを指定することができます。
既存のアプリをアップデートする場合は、Ti.Database.installを上のようにしてあげて、アプリの起動時に古いデータファイルを新しい場所に移動してあげるとかしないといけませんね。途中、スペースを含むパスがありますのでこんな風に指定します。
var db_file = 'my_app.sql';
var old_file = Ti.Filesystem.applicationDataDirectory + '../Library/Application%20Support/database/' + db_file;
var new_file = Ti.Filesystem.applicationDataDirectory + '../Library/Caches/' + db_file;
var old = Ti.Filesystem.getFile(old_file);
if(old.exists()){
old.move(new_file);
}
Popularity: 6% [?]
Titanium Mobileでアプリがリジェクト?!
Appleのアプリ検証ツールのバグでTitanium Mobileで作成されたアプリがリジェクトされるようです。回避方法が発表されていたのでざっくり訳しておきました。
再度追記:Appleが審査用ツールを更新してこの問題はなくなったそうです。もう大丈夫。
追記:なんと、この方法ではうまくいかなかったとのこと。
Update: 2:42pm 11/13/11
Appleの審査でのスキャンを回避する修正がCIサーバ上の全てのリリースブランチでご利用頂けるようになりました。こちらのビルドでアプリを再度申請して頂ければ問題を解消することが出来るはずです。ただし、現在も私たちは今後アプリが「プライベートAPIの利用」と判定されることがないよう、申請済みのアプリでこの修正をテストしています。Update 6:11pm 11/12/11:
下記の方法では問題は解消されないことが判明しました。現在、Appleの審査で「プライベートAPI」の利用と誤判定されるのを回避できるかどうか、別の方法を内部テスト中です。ご不便をおかけしておりますが、この問題の解決を最優先として取り組んでおります。元の投稿:
登録したアプリがプライベートなAPIを利用しているというAppleからのフィードバックについてコミュニティから何件か報告がありましたが、まだ「layout」であるとしか確認されていません。TitaniumのアプリケーションはプライベートなAPIを利用してはいないので、私たちはこれはAppleのアプリ解析ツールに何か変更があり、それが間違ってTitaniumのアプリのプロパティを検知しているのではないかと考えています。Appleの解析ロジックにおけるこの不具合の回避方法ですが、私たちは実装ロジックから「layout」というキーワードの使用を避ける新しいビルドを用意しました。現在もこの修正が正しいものかどうかAppleのApp Storeで検証しているところです。この特別版ビルドのインストールして利用するには、下記の手順に従ってください。
今回のエラーはAppleの処理ロジックが原因でしたが、皆様がApp Storeへの登録作業を問題なく出来るようにするのが私たちの希望です。本件についてご質問、フィードバックなどございましたら、直接私(kwhinnery at appcelerator dot com)までご連絡頂ければ喜んでお手伝い致します。
というわけで、インストール方法はこちらの手順に従ってください。
Popularity: 3% [?]
VMware FusionでBootcampは使うな
今までずっとVMWare FusionでBootcamp領域のWindowsを起動していたのだけれど、正直なところ遅すぎてあまり使い物にならなかった。そもそも、Windows用クライアントしか用意されていないYAMAHAのルータ経由のVPNを利用するなど特殊な用途でVMwareを起動して使っていたのだけれど、ただでさえ面倒くさいその手の作業が本当にストレスになるほどVMware FusionのWindowsは遅かった。
で、HDDが激安な昨今、MacbookProをSSDにするか悩んだ末に、メインマシンなので容量重視と1TBのHDDにしたのをきっかけに、ふと複数のWindowsの試験環境を作成してみたのだが、イメージから起動するWindowsの速さに驚いた!ぜんぜんストレスなく使える!Bootcamp領域のWindowsの遅さでよく調教されてしまったせいかもしれないが、それでもいいじゃないか!
というわけで、Bootcampに入れたWindowsの遅さで白髪が増えたという方は、一度普通にイメージでVMを作成してみるといいと思いますよ。
Popularity: 2% [?]
Phone Story
The Yes Menのアプリが登場した。
Phone Storyはスマートフォン製造の隠れた社会的コストを学ぶ教育ゲームです。カラフルなミニゲームを通じて、あなたの電話がコンゴのコルタン鉱山からパキスタンの電子部品廃棄現場へと巡る旅を追いかけてみましょう。終わりのない技術の陳腐化スパイラルを強いるマーケットの力と競争です。
あなたが自分でやっていることが世界にどんな影響を及ぼしているのか忘れないよう、お好きなデバイスにPhone Storyを入れておきましょう。このアプリの売り上げから得られる利益は全てゲームの中に登場する問題の解決に向けて活動している団体に寄付されます。
いつまで入手可能な状態なのか不明なので、あなたが世界を汚染するスマートフォンのオーナーであり、それがAppleの製品であるなら、このゲームをAppleがAppStoreから消してしまう前にゲットしよう!
Popularity: 2% [?]
iOS向けアプリでやってほしくない仕様変更
そんなにたくさんアプリを作ってきたわけではないので、これは偏った意見かもしれませんが、iOS向けアプリを作っていて、こんな仕様変更は困るな、というのがいくつかありました。
たとえばアプリ内の特定の機能はIn-App purchaseを利用して購入が完了しないと使えない、という仕様はよくあると思います。この購入フローで、購入手続きが完了したら、前の画面の見栄えが変わっていたりするとかいうのは勘弁してほしいです。全く不可能というわけではもちろんないのですが、不要なレベルで複雑化しがちになってしまいます。
こんな感じ。ほら、購入して戻ると前の画面が勝手に変更されてるでしょ?
最初から計画されていたら構わないんですが、後からこういうのを追加するのが一番困ってしまいます。ウェブから転身した人だと、こんなのJavaScriptで親ウィンドウのノードを書き換えるだけみたいに考えたりしがちなのですが、どちらかというとiOSとかAndroidのアプリだと、画面遷移に「戻る」ボタンを多用すると考えた方がしっくりくるかもしれません。戻るボタンを押して戻った先の画面の内容を書き換えろ、というのはちょっと変だな、って思いませんか?
前にも書きましたが、こういうアプリって、画面をスタックに積んでは降ろすという性質があって、ひたすら前進することを基本にしているウェブとはちょっと違うんですよね。これでうまく伝わるといいんですが。
Popularity: 1% [?]
Titaniumで作ったアプリの表示名を国際化対応(iOS)
Titaniumで作成したアプリの表示名はデフォルトでtieapp.xmlのnameで指定されたものになる。iPhoneだと生成されるInfo.plistではCFBundleDisplayNameが${PRODUCT_NAME}になっている。ここにはデフォルトではプロジェクトディレクトリの名前がそのまま入るのだが、例えば日本語のアプリ名にしようとすると、プロジェクト名は日本語に出来ないので、iPhoneなら一度ビルドして作成されるbuild/iphone/Info.plistをプロジェクトのディレクトリ直下にコピーして、CFBundleDisplayNameを書き換える。AndroidだとAndroidManifest.xmlを同じくコピーして中身を書き換える。
でも、このやり方だと画面に表示するアプリの名称を国際化対応することができない。そこで調べてみたら、iOSだとこんなやり方でできた。
(1)例えば英語ならResources/English.lproj/InfoPlist.stringsを作成する
(2)中身を下のようにする。
CFBundleDisplayName = "English Name!";
(3)このInfoPlist.stringsをUTF-16で保存する。
(4)念のためbuild/iphone/以下のファイルを全部消してリビルドする
日本語の場合はJapanese.lprojになる。ところで中国語だが、zh_TW.lprojが繁体字、zh_CN.lprojが簡体字になる。Info.plistはそのままCFBundleDisplayNameは${PRODUCT_NAME}で大丈夫。
Popularity: 2% [?]


