ほげぐらまの別館

プログラムに限らずてきとーに、ね?

APNS(Apple Push Notification Services)は書くまでもないと思いますが「プッシュ通知」によりアプリケーション提供側から任意のクライアントにショートメッセージなどでユーザに通知を行う機能です。この機能を使えばアプリ提供者側からの通知はもちろん、ユーザ間のやり取りなど幅広く用いることができます。で、既にこの機能自体がiOS3.0以降で組み込まれているので別段新しい機能、という訳でもないのです。が、一方で多くのユーザにお知らせとしてAPNSを送ろうとすると意外に時間がかかるという・・・・。

あまり知られてはいないようなのですが、APNSを1回のSSLコネクションを通じて複数のユーザ・クライアントに飛ばすことが可能です。APNSで使用されるプロトコルは非常に有名なチュートリアルサイト

How to build an Apple Push Notification provider server (tutorial)で案内されている通りです。複数に送信する場合は、単純にこのメッセージ部を繰り返せばいいようです。プログラム的には以下の感じ。


$devices = (array)$devices;
foreach ($devices as $device) {
  $apnsMessage = chr(0) . 

    // token length
    pack('n', 32) . 

    // device token
    pack('H*', str_replace(' ', '', $deviceToken)) . 

    // payload length
    chr(0) . chr(strlen($payload)) . 

    // payload
    $payload;

  // send apns message
  fwrite($apns, $apnsMessage);
}

但し、注意ごととしては以下の通り。

  • (複数クライアント送信に限ったことではないですが)Payloadが256Byteを超えてはならない
  • 1回の通信で全パケットが約7000Byteを超えるとサーバから切断される、らしい (←どこかで見た・・・)

なので、1メッセージが以下のような最長だとすると・・・・?

  • 1byte – コマンド (token)
  • 4byte – デバイストークン長
  • 32byte – デバイストークン
  • 1byte – コマンド (payload)
  • 1byte – ペイロード長
  • 256byte – ペイロード

295byteとなり、仮にサーバ切断となる7000byteまで達するには20人程なら余裕で許容できる範囲となるはずです。多分、10人程度なら超余裕ではないのかな、と。

APNSに関してはGameCenterやInApp-Purchaseの不具合とは異なってSandboxでも非常に安定をして動きます。なので、リリース(Distribution)前にしっかりと確認を行うことができるのでパフォーマンス調整を行ってみると良いかもしれません。

iOS AppのInAppPurchaseの処理、となると大抵の場合はAppleから提供されているサンプルコードを利用すれば異なります。もちろん私もこのコードを使いながらも自分が使いやすいように購入部分のみをシングルトンクラスへ処理を移して、


Environment *environment = [Environment instance]
assert(environment);

BOOL request = [environment requestPurchase:@"MY_PRODUCT_ID"];
if (request == NO) { 
  UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:....] autorelease];
  [alert show];
}

 

のようなコードを打てば好きなタイミングでプロダクトIDさえ渡せば処理は勝手にやってくれます。購入後にサーバ側で購入に対するデータ処理は行ってくれるので特にクライアントはそれ以上の処理が必要ないぢゃんと踏んでいたのですが・・・・。バカだったと反省した今日この頃です。となると、最終的にAppleサンプルでいうcompleteTransaction, failedTransactionの部分にデリゲートをコールするなんていう仕掛けを追加しなければならないのですが、結構デリゲート嫌いなんです。特に、今回のような1デリゲートに1プロトコルしか必要のない時は。

ここで思い出したのが、最近GameCenterで見かけたコール部分に関数定義を渡すような方法。この名前を知らずにググっていたらStackoverflowに引っかかりました。

これを見ると単純に”blocks”としか書いてないですが、日本語の場合はブロック構文というみたいですね、多分。正直、このサンプルを見たときには意味不明な構文過ぎて理解しがたかったですが、それを除いても使う価値はあるものだな、と。ちなみに、私が書くとこんな感じになります。


Environment *environment = [Environment instance]
assert(environment);

BOOL request = [environment requestPurchase:@"MY_PRODUCT_ID" callback:^(BOOL result){
  // ここで購入後の適切な処理をする
}];

// ここのエラーブロックはリクエストそのものに失敗した場合
if (request == NO) { 
  UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:....] autorelease];
  [alert show];
}

-------

@interface Environment

void (^_cbPurchaseHandler)(BOOL result);



@implements Environment

- (void)requestPurchase:(NSString *)product_id callback:(void(^)(BOOL))cbHandler {
  // 適切なproduct_idかどうかを調べたり、各種購入の前確認を行う
  ...(略)...
  
  if (invalid) {
    return NO;
  }

  // コールバックのハンドラーをコピーする
  _cbPurchaseHandler = [cbHandler];
  
  // 購入処理を走らせる
  [[SKPaymentQueue defaultQueue] addPayment:payment];
  
  return YES;
}

- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
  // Your application should implement these two methods.
  [self recordTransaction: transaction];

  // コールバックを呼び出して購入処理に対する付与処理を行う
  _cbPurchaseHandler(YES);
  [_cbPurchaseHandler release];
  _cbPurchaseHandler = nil;
  
  // Remove the transaction from the payment queue.
  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
  // キャンセルも含めて購入が失敗したことをコールバックに通知する
  _cbPurchaseHandler(NO);
  [_cbPurchaseHandler release];
  _cbPurchaseHandler = nil;

  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

 

あ、所詮ほげったほげぐらまが書くコードなのであまり突っ込まないでね。いつもほげほげしていますが、今日はいつになく有意義なものを覚えました、とさ。なんでこんな回りくどい実装をしたかというと、プロダクトIDを渡して購入処理をする部分(恐らく大抵のアプリで共通)、とその結果を受けて付与する部分(アプリ固有の処理)で分けたかったから。

大層な処理ブロックでもないのでコピーすればいいぢゃん、なんですがそこは宗教が許さないのでしょう。だって宗教なんだもん・・・・

予約した時点で店員には釘を刺されましたし、11月4日のTwitterによる惨状を聞いているだけに予感はしていました。が、先日ビックカメラより電話で直々に発売日のお渡しが不可能との連絡がありました。理由は単純明快で、生産数に対して予約数が非常に膨大で製品数が追い付かない、とのこと。Vitaの予約は余裕だったので恐らく生産台数はかなり有るのだろうとは思いますが、3Dヘッドマウントディスプレイは予測できなかったのかと。

で、お渡し不可能だけの通知を受けるだけではつまらないので、電話対応の店員にHMZ-T1話を聞くと大よそ自分の予約が30番目前後だったそうです。あと何番目程度で入りますか?という質問には残念ながら答えてくれませんでしたが。まぁ、入荷数がわかってしまいますものね。現状では12月末~年越し1月が濃厚とのことですが、なんとかクリスマスには手にしたいな、というところです。

オークションという手ももちろんありましたが、転売オークションは腐っても手にしたくない物体なので首を長くして待つことにします。

たぶん誰しもが思っていることだろうと思いますが、Adobe製品の修正パッチがウザいです。本日もPC起動早々にReaderの修正パッチが降ってきました。確かにセキュリティ脆弱があったとして、それを突かれて被害に遇うのは論外なのでしょうがそもそもPDFの表示にそこまでの機能を求めているのかな、と。

私のReaderの利用用途は電子コミックやドキュメントなど別段セキュリティPDFやフォーム入力などの特殊なものでもなく、普通に文字と画像が表示されればそれでいいレベルなんですが・・・・。

せめて、表示だけの部分をするコアコンポーネントと、それ以上に事をする拡張コンポーネントに分けて、ユーザがそれを選択できるようにすればいいと思うのですが。中身はCOMコンポーネントのはずなんだからそれくらい簡単にできるっしょ、と思うのは間違いなのかな。

 

と、書きつつも、所詮Adobeだし。みんな口をそろえて言うこと。

「MacromediaのFireworksは最高だった。特に最後のリリースとも言えるFireworksMXは。Adobeに買収されてCSシリーズになりだした辺りから商品が腐ってきた」

そんな私は今でもFireworksMXの愛好家です。

blogのネタにするのも悲しいほどですが頭痛で寝ていました。私の頭痛は特徴がはっきりしていて、こんな感じです。
・大抵発生するのが土日(+お休みの日)のどちらか。
・頭痛が出るのは起きてから2、3時間程経過してから。
・痛みは万力で締め付けられるような感じが絶え間なく続く感じ。

以前に病院にかかった時には緊張性頭痛の典型的なパターンだと言われて、SG顆粒という一般的な?薬を処方されました。原因としては、普段から脳が緊張状態にあったものが土日で急に弛緩することで起こるそうな。加えて、毎日飲んでいるコーヒーを忘れた時には更に発症率が高くなるという、、、。

って事で割と注意しているはずなのに本日コーヒー飲み忘れて頭痛により休みを棒に降ってしまう残念な結果に。西洋医学は基本的に嫌いなので、飲まずに我慢したってのもありますけどね。
ともあれ原因自体は普段の高タスク状態が続いているってのが根本であって、これを分散化できない事には話にならないな、と。

3Dヘッドマウントも割と絶望的な状態なので、明るいニュースが無いかなー、という状態です。