みなさんこんにちは。こんばんわ。スマートフォン事業部のイッシーです。スマートフォンアプリを開発しているスマートフォン事業部のイッシーです。スマートフォンという単語を乱発してますが今回はスマートフォンの話だからです。ʕ→ᴥ←ʔ

今回はiPhoneにおけるアプリ内課金のお話です。なんか敷居が高いですね!でも実装は意外と簡単です。では早速。

■フレームワーク

StoreKitが必要になります。

#import <StoreKit/StoreKit.h>

■クラスの定義

以下のようにしてSKProductsRequestDelegateとSKPaymentTransactionObserverを実装します。ビューコントローラでなくても大丈夫です。ちなみに今回はローディング中の処理も解説コードに入れてます。

@interface HogeViewController : UIViewController<SKProductsRequestDelegate, SKPaymentTransactionObserver> {
    bool isLoading;
    SKProductsRequest       *skProductsRequest;
    UIActivityIndicatorView *spinner;
    UIView                  *loaderBg;
    UILabel                 *loaderTitle;
}
@property bool isLoading;
- (IBAction)purchaseButtonPushed:(id)sender;
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response;
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransaction:(NSArray *)transactions;
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);

■実装

購入ボタンが押されたときの処理

ここから購入処理の通信が始まります。UIActivityIndicatorViewを使うと簡単にロード中の表示ができるのですが、背景色によっては全く見えない場合がありますので、1枚UIViewを下に敷いて見やすくしましょう。また、1点注意があるのですがUIによっては購入ボタンの2度押しが可能ですので、フラグを立ててしっかり防ぐようにしましょう。

- (IBAction)purchaseButtonPushed:(id)sender {
    if([[UIDevice currentDevice] networkAvailable] == NO){
        return;
    }
    if(self.isLoading){
        return;
    }
    if([SKPaymentQueue canMakePayments]){
        self.isLoading = true;
       
        // loader
        loaderBg = [[UIView alloc] init];
        [loaderBg setFrame:CGRectMake(100, 150, 120, 85)];
        [loaderBg setBackgroundColor:[UIColor blackColor]];
        [loaderBg.layer setCornerRadius:13.0f];
        [loaderBg setAlpha:0.7];
        [self.view addSubview:loaderBg];
       
        loaderTitle = [[UILabel alloc] init];
        [loaderTitle setTextColor:[UIColor whiteColor]];
        [loaderTitle setFrame:CGRectMake(0, 57, 120, 18)];
        [loaderTitle setText:@"Loading..."];
        [loaderTitle setTextAlignment:UITextAlignmentCenter];
        [loaderTitle setBackgroundColor:[UIColor clearColor]];
        [loaderTitle setAlpha:1.0];
        [loaderBg addSubview:loaderTitle];
       
        // spinner
        spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
        [spinner setCenter:CGPointMake(self.view.frame.size.width/2.0, self.view.frame.size.height/2.0)];
        [self.view addSubview:spinner];
        [spinner startAnimating];
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
       
        // 課金部分
        // identiferを元にappleサーバに問い合わせます
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        skProductsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"identifer"]];
        [skProductsRequest setDelegate:self];
        [skProductsRequest start];
    }
    else{// 本体の設定でアプリ内課金をOFFにしている人向けの表示
        [self showAlert:@"cannot purchase" text:@"設定 > 一般 > 機能制限で[App内での購入]をONにしてください"];
    }
}

アプリ内課金は本体の設定で無効にできますので、必ず分岐してあげましょう。

appleのサーバにidentiferをお問い合わせした結果の処理

なんてことはありません。identiferをセットしてリクエストしたレスポンスに商品データがあれば、その商品の購入手続きに入ります。

#pragma mark -
#pragma mark SKProductsRequestDelegate
- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    if (response == nil) {
        return;
    }
    for(SKProduct *product in response.products){// productを元にした購入オブジェクトをキューに入れて購入手続きに入ります
        SKPayment *payment = [SKPayment paymentWithProduct:product];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
}

ここの部分で何度か購入を繰り返しているとクラッシュしてしまう人もいるかもしれません。それは最後述の方法で回避できます。

購入のトランザクションの状態が変わったときに呼び出される処理

状態はswitch文で分岐しています。完了した時の部分にアプリに合わせた処理を呼び出してあげましょう。

#pragma mark -
#pragma mark SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransaction:(NSArray *)transactions {
    BOOL isFinished = YES;
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:// 何らかのOKを押す前の処理
                break;
            case SKPaymentTransactionStatePurchased:// success : 決済手続き完了処理
                [queue finishTransaction:transaction];
               
                // もし自社サーバでユーザが購入を完了したかどうかappleサーバに確認する場合は、
                // transaction.transactionReceiptの値をbase64に変換して自社サーバに送信します。
                // [transaction.transactionReceipt base64EncodedString];
               
                isFinished = NO;
                break;
            case SKPaymentTransactionStateFailed://  途中でキャンセルした時
                isFinished = NO;
                [queue finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:// 通常はコールされない
                isFinished = NO;
                [queue finishTransaction:transaction];
                break;
            default:
                break;
        }
    }
    if (isFinished == NO) {// トランザクションが何らかの完了をした時=>ローディングを消す
        self.isLoading = false;
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        [loaderTitle removeFromSuperview];
        [loaderBg removeFromSuperview];
        [spinner stopAnimating];
    }
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){
    [self paymentQueue:queue updatedTransaction:transactions];
}
サーバ側

ユーザから送信されてきたレシート情報を自社サーバ側でappleに確認するには、以下のようなコードで処理してください。

$ch  = curl_init();
$url = "https://sandbox.itunes.apple.com/verifyReceipt";// サンドボックス(テスト用)
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"receipt-data" : "' . $receipt . '"}');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_HEADER, FALSE);
$result = curl_exec($ch);

テスト

xcodeからインストールしたアプリではサンドボックス経由という事が購入ダイアログで表示されます。storeからインストールしたアプリでは本番と同じダイアログになります。特にコードを変更する必要はなく自動で判定してくれるようです。

最後に

だいたいOKなんですが実は複数回購入手続きをするとクラッシュします。dealloc部分で課金処理キューからオブザーバーをしっかり削除するようにしましょう。

- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];// これ!!!
    [loaderBg release];
    [loaderTitle release];
    [skProductsRequest release];
    [spinner release];
    [super dealloc];
}

実はここの部分でクラッシュの原因が分からず凄く苦労しました。(,,Ծ‸Ծ,, )

こんにちは。甘いお菓子が大好きなイッシーです(´〜`)mgmg。世は既にバニラアイスの季節ですね!

今回はバニラアイスとは関係がありませんが、Gitでのバージョン管理をほんの少しだけ掘り下げてブランチを使ってみることにします。ブランチとはmasterから派生し枝分かれした部分です。木の枝と違うのは枝分かれした部分が幹に融合する事があることでしょうか。ブランチを使えばビジネス的な承認が得られていないバージョンをそのままにしておき、次のバージョンに向けた作業を始めることが簡単にできます。効率化万歳\(^o^)/

では早速、以下のようにしてhogeという名前のブランチを作ります。

git branch hoge

コレでおしまいです。簡単ですね。以下のコマンドで作ったブランチを確認しましょう。

git branch hoge

以下のように表示されます。

* master
  hoge

アスタリスクがついているものが現在選択されているブランチです。新しく作ったブランチに変更を加えるには、ブランチを切り替える必要があります。

git checkout hoge
git branch

以下のように表示されると思います。

  master
* hoge

以後の作業は全てhogeブランチに反映されるとこになり、何もしなければmasterに変更を及ぼすことはありません。checkoutをすることによってmasterと並行して作業をすすめることができます。

■マージ

枝分かれした方の変更点をmasterブランチに反映させたい時があります。そんな時に使うのがマージです。

説明のための作業

以下のコマンドでmasterブランチに切り替えたあと、ファイルを追加しコミットします。

git checkout master
touch public_html/gomi.html
git add .
git commit -a -m "add trash file for the test"
[master 94cf4d1] add trash file for the test
 1 files changed, 35 insertions(+), 0 deletions(-)
 create mode 100644 public_html/trash.html

以下のコマンドでhogeブランチに切り替えたあと、ファイルを追加しコミットします。

git checkout hoge
touch public_html/gomi.html
git add .
git commit -a -m "add gomi file for the test"
[hoge b6e5b56] add gomi file for the test
 1 files changed, 35 insertions(+), 0 deletions(-)
 create mode 100644 public_html/gomi.html

お待たせしました。マージです。以下のコマンドでhogeブランチで行った変更をmasterにマージします。

git checkout master
git merge hoge
Merge made by recursive.
 public_html/gomi.html |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)
 create mode 100644 public_html/gomi.html

圧縮コミット

バグフィックスなどの際にブランチを作りまとめてコミットしたい場合、変更履歴をまとめて1つにしコミットしたいですよね。そんな時は以下のようにマージします。

git checkout master
git merge --squash hoge

チェリーピック

別ブランチの特定の1コミットをマージしたい場合は以下のようにします。

git checkout master
git cherry-pick b6e5b56

別ブランチで作業をしてくれている人がバグを潰してくれた場合もコミット名が分かれば簡単にマージできます。

■タグ

目印のようなものでリリースなどの際にはタグをつけておいた方が分かりやすいと思います。

git tag 1.0 hoge

いかがでしょうか?ブランチを作っておけば良かったかもとか思う今日この頃です。ちなみにですが以下のコマンドで特定のコミットを元にブランチを作ることができます。

git branch hoge b6e5b56

とまぁできるのですが、その時にやるのがやっぱり一番なのかなと・・・そんなこんなでバージョン管理は慣れが凄く大切だと思います。(。・ω・)ノ゙

こんにちは!イッシーです(。≖ิ‿≖ิ) Apacheのプロセス数が増えなくてかなり困ったのでメモっておきます。

■通常設定

まずは通常の設定ファイルを開きます。

vi /etc/httpd/conf/httpd.conf

以下のような部分を探しましょう。

<IfModule prefork.c>
MaxClients       150
StartServers     5
MinSpareServers  5
MaxSpareServers 10
</IfModule>

MaxClientsの数を確認しましょう。各ディレクティブの意味は以下のとおりです。

MaxClients
応答することのできる同時リクエスト数。
StartServers
起動時に生成される子サーバプロセスの数。但し、プロセス数は動的に調整される。
MinSpareServers
リクエストを受けていないサーバプロセスの最小個数。
MaxSpareServers
リクエストを受けていないサーバプロセスの最大個数。

設定が終わったら以下のコマンドで再起動します。

/etc/init.d/httpd restart

大概はここまでの内容で上手く反映されるかと思います。。。で、今回は反映されなくて凄く困りました。(☍﹏⁰)

■現在の設定の確認

読み込まれて有効になっている設定を確認します。

vi /etc/httpd/conf/httpd.conf

以下のような部分を探しコメントアウトを削除します。但し、以下のようにした場合は全ての場所からサーバの情報が見えてしまいますので、必要に応じてIPなどで制限するようにしてください。

<Location /server-info>
    SetHandler server-info
    Order deny,allow
    Deny from all
    Allow from all# you need restrict by IP address
</Location>

変更が終わり保存したらサーバーを再起動します。

/etc/init.d/httpd restart

その後ブラウザからアクセスします。

http://127.0.0.1/server-info

読み込まれているモジュールの一覧などが表示されますので、prefork.cをクリックします。以下のように表示されました。

Module Name: prefork.c
In file: /etc/httpd/conf/httpd.conf
  78: StartServers 5
  79: MinSpareServers 5
  80: MaxSpareServers 10
  82: MaxClients 150
In file: /etc/httpd/conf.d/swtune.conf
  78: StartServers 5
  79: MinSpareServers 5
  80: MaxSpareServers 10
  82: MaxClients 10

もう1ファイル読み込まれていることが分かります。そしてMaxClientsの値が上書きされていたことも分かりました。

■設定ファイルの変更

もうできたも同然ですね(♡´ω`♡)。以下のコマンドで設定ファイルを編集します。

vi /etc/httpd/conf.d/swtune.conf

MaxClientsの値を変更したら以下のコマンドでサーバーを再起動します。

/etc/init.d/httpd restart

キタ━━━━。゚+.ヽ(´∀`*)ノ ゚+.゚━━━━!!。ということで今回はライトに締めくくります。ではではー

みなさんこんにちはこんばんは。イッシーですʕ→ᴥ←ʔ。iPhoneのブラウザではCSS3のアニメーションや3D効果が使用できます。そこで今回はCSS3を使って様々な効果を作ってみましょう。Google Chromeなどのブラウザでも見ることができますので様々な効果を楽しみつつ、様々な場で使ってみてください。

■角丸

特に目新しくありませんが、見栄えが良くなるのでとりあえず角丸にしておきます。

.element{
    border:1px solid #333333;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;
}

ʕ→ᴥ←ʔ キュンキュン!

■影

CSS3では要素に影をつけることができます。

.element{
    border:1px solid #333333;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;
    -webkit-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -moz-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
}

ʕ→ᴥ←ʔ キュンキュン!

テキストに影をつけることもできます。

.element{
    border:1px solid #333333;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;
    -webkit-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -moz-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
}
.element p {
    -webkit-text-shadow: 0 -1px 1px #FFF, -1px 0 1px #FFF, 1px 0 1px #000;
    -moz-text-shadow: 0 -1px 1px #FFF, -1px 0 1px #FFF, 1px 0 1px #000;
    text-shadow: 0 -1px 1px #FFF, -1px 0 1px #FFF, 1px 0 1px #000;
}

ʕ→ᴥ←ʔ キュンキュン!

■アニメーション

お待たせしました。アニメーションです。

.element {
    border:1px solid #333333;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;
    -webkit-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -moz-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -webkit-transition-property: opacity;
    -webkit-transition-duration: 1000ms;
}
.element:hover {
    opacity:0;
}

マウスオーバーするとアニメーションします。

ʕ→ᴥ←ʔ キュンキュン!

3D効果

以下のようにすることで立体的に回転させることができます。

.element {
    border:1px solid #333333;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
    border-radius:10px;
    -webkit-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -moz-box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    box-shadow: 10px 10px 20px #bbbbbb, -3px -3px 10px #bbbbbb;
    -webkit-perspective: 100;
    -webkit-transition-property: -webkit-transform;
    -webkit-transition-duration: 3000ms;
    -webkit-transform: rotateY(0deg);
    -webkit-transform-style: preserve-3d;
}
.element:hover {
    -webkit-transform: rotateY(180deg);
}

マウスオーバーするとアニメーションします。

ʕ→ᴥ←ʔ キュンキュン!

■JavaScriptイベント

CSS3によってプログラミングすることなく様々な表現ができるようになりました。しかし、もっと複雑なことをするにはJavaScriptとの組み合わせが必要になってきます。まずは以下のようにアニメーションの終了をキャッチしましょう。

var elm  = document.getElementById('card2011052906');
var text = document.getElementById('card2011052906text');
var flag = false;
elm.addEventListener(
    'webkitTransitionEnd',
    function(){
        flag = false;
        text.innerHTML = 'かいてんしゅーりょー';
    },
    false
);
elm.addEventListener(
    'click',
    function(){
        if(!flag){
            flag = true;
            elm.style['-webkit-transform'] = 'rotateY(180deg)';
            text.innerHTML = 'かいてん中';
        }
    },
    false
);

見ての通りCSSによるアニメーションが終了すると、webkitTransitionEndイベントが発火します。(クリックしてください)

ʕ→ᴥ←ʔ キュンキュン!

これを応用してカードを裏返す効果を演出してみます。(クリックしてください)

ʕ→ᴥ←ʔ

コードは以下のようになります。

var counter = 0;
var emoticon = ['ʕ→ᴥ←ʔ', 'ʕ•͡ᴥ•ʔ'];
var elm = document.getElementById('card2011052907');
var flag = false;
var rotate90 = function(){
    elm.removeEventListener('webkitTransitionEnd', rotate90);
    elm.getElementsByTagName('p').item(0).innerHTML = emoticon[++counter % emoticon.length];
    elm.style['-webkit-transform'] = 'rotateY(180deg)';
    elm.addEventListener(
        'webkitTransitionEnd',
        rotate180,
        false
    );
}
var rotate180 = function(){
    flag = false;
    elm.removeEventListener('webkitTransitionEnd', rotate180);
}
elm.addEventListener(
    'click',
    function(){
        if(!flag){
            flag = true;
            elm.style['-webkit-transform'] = 'rotateY(90deg)';
            elm.addEventListener(
                'webkitTransitionEnd',
                rotate90,
                false
            );
        }
    },
    false
);

90°までCSSで回転させJavaScriptで表示を入れ替え、さらに90°回転させています。

■まとめ

いかがでしょうか?丸角に画像を使っていた時代が懐かしいですね。CSS3を使えばデザイン性が向上するだけでなく、不要な画像を減らすことができます。また、Mobile SafariではCSS3の一部の機能がGPUによって処理されるため、setTimeoutを使用したjQueryのアニメーションよりもスムーズなアニメーションになります。

■注意

Mobile Safari

上述のカードのサンプルは少し修正するだけで、画像を裏返すといったものに変更が可能です。しかし、img要素にbox-shadowを適用してしまうとMobile Safariで処理に時間がかかってしまい、アニメーションが止まってしまいます。そういった場合は外側のブロック要素に対してbox-shadowを適用するようにしてください。

android

android2.1(実機・エミュレータ)のブラウザは3D Transformsは動作はします。しかし、GPUの支援がないのでカクカクしたアニメーションになります。また、android2.2(エミュレータ)のブラウザでは回転方向が異なってしまい使い物になりませんでした。この不具合はandroid3.0(エミュレータ)では修正されていました。androidのブラウザには本当に頑張ってほしい限りですね。

こんにちは。イッシーです(๑°⌓°๑)!今回は前回の続きでAWSで立てたサーバーを簡単に複製できるようAMIを作成したいと思います。社内ドキュメントをHTMLで記録しておこう的なノリです。ちなみにAmazon Linuxを使用しております。

そういえば、前回書き忘れていたので以下のコマンドでルートのパスワードを設定しましょう。

sudo passwd root

■証明書の取得

管理画面にアクセスして、「X 509証明書」で「秘密鍵」と「証明書」をダウンロードしましょう。

サーバ上でamazon コマンドを実行していく感じですね!

■証明書の転送

上述でダウンロードした2ファイルをサーバ上に転送しましょう。scpでもgitでもOKです。

■AMIの作成

Account Activityにアクセスして右上にある口座番号(ハイフン付き)をメモってください。それではAMIを作成します。

su
ec2-bundle-vol -d /mnt --privatekey [秘密鍵ファイルパス] --cert [証明書ファイルパス] --user [口座番号] --fstab /etc/fstab

使用している容量によりますが、僕は10分くらい時間がかかりました。

■S3へ転送

管理画面にアクセスして、「アクセスキーID」と「シークレットアクセスキー」をメモしてください。次に以下のコマンドを実行しs3のバケットに転送します。

ec2-upload-bundle --bucket [バケット名] --manifest image.manifest.xml --access-key[アクセスキーID] --secret-key [シークレットアクセスキー]

僕は1時間くらい時間がかかりました。ネットサーフィンでもして・・・違った、別のタスクをこなして終了を待ちましょう。

■AMIの登録

s3に転送したファイルからEC2を起動できるようにします。以下のコマンドでAMIを登録しましょう。

ec2-register [バケット名]/image.manifest.xml

もしくは以下のコマンドで登録します。

ec2-register [バケット名]/image.manifest.xml --privatekey [秘密鍵ファイルパス] --cert [証明書ファイルパス]

実行するとIDが出力されるのでメモっておいてください。

登録したAMIを確認

ec2-describe-images -o self

登録したAMIを削除

ec2-deregister [AMIのID]

個人的な感想ですが、AWSは他のサービスよりも柔軟性も高く機能も豊富です。そのため固有の操作というものが少なからずありますが、インフラを0から構築するよりははるかに楽なので頑張って覚えていきたいところです。‎(๑╹ڡ╹)