Skip to content

はてなブログへ引っ越しします

2014年、あけましておめでとうございます。本年もよろしくお願いいたします。

お知らせ
さて、思うところがあり、はてなブログへ引っ越しすることにしました。

おおばログ

これまでのエントリ(…といっても大した数はないのですが)はこちらに置いたまま、新しいエントリははてなブログに更新していきます。

「iOSアプリ内課金+広告」の本を共著で書きました

「アプリ内課金+広告 iPhoneプログラミング」という本を、酢酸センセーこと和田健司さん(@ch3cooh)と共著で書きました。秀和システムさんから2014年1月下旬に発売予定です。
アプリ内課金+広告 iPhoneプログラミング
私は主にアプリ内課金に関するパートを、和田さんは広告パートを執筆しました。iOS 7にも対応していますし、アプリに広告を載せてチョットでもお小遣い入ったら嬉しいという方や、アプリ内課金を初めて実装したいという方にオススメできる1冊です。

目次
目次はこんな感じです。

Chapter 1 アプリ内課金と広告を理解する
アプリ内課金とアプリ内広告が存在する背景を紹介しつつ、それぞれについて説明をします。またアプリ内広告とアプリ内課金を組み合わせる魅力についても説明しつつ、この組み合わせを採用しているアプリを紹介します。

Chapter 2 アプリ内課金を実装するための準備
アプリ内課金をアプリに実装する前に準備が必要です。開発者向けに提供されている Web サイト、iOS Developer Portal、iTunes Connect を使って事前準備を行うための説明をします。

Chapter 3 アプリ内課金の実装:単一の非消耗型
アプリ内課金を実装するうえで理解をしておくべきポイントとして、アプリ〜 Store Kit 〜 App Store の関係性、各クラスの位置づけ、実装の基本ポイントを説明します。

Chapter 4 アプリ内課金の実装:単一の消耗型と自動更新購読型
アプリ内課金でよく使われている「消耗型」と比較的使われている「自動更新購読型」 について、実装のポイントを説明します。

Chapter 5 さまざまなタイプの広告を実装する
アプリ内広告を掲載するには、広告を配信する各種広告会社との契約、SDKの導入 が最初に必要です。またアプリ内での広告の見え方(見せ方)も実装の前に知っておい た方がよいでしょう。契約、SDKの導入、アプリ内広告の実装について説明します。

Chapter 6 アプリ内課金+広告タイプのアプリを作る
広告とアプリ内課金を組み合わせた(開発者にとって)魅力のあるアプリについて、 実装方法を具体的に解説します。

Chapter 7 複数の課金パターンを提供するアプリを作る
1 つのアプリ内に複数のアプリ内課金パターンを入れることで、アプリの魅力を広げることができます。このようなことを実装するにあたってのポイントを解説します。

Chapter 8 アプリ内課金をテストする
アプリ内課金はエンドユーザが支払いをする性質上、課金処理をしっかり作った上で動作確認をしておく必要があります。動作確認のポイントを説明します。

Chapter 9 アプリ内課金と広告のトラブルシューティング
アプリ内課金、アプリ内広告の実装以外に、意外なところで落とし穴が出てきたりします。多くの人が経験するであろうトラブルについて、その原因と対策について説明します。

苦労したこと
執筆当時にリリースされたiOS 7ですが、アプリ内課金のレシート確認方法がiOS 7からややこしい方法に変わっていることに気がつきました。それの解決方法について当時は日本にも海外にも情報が皆無でしたが、なんとか方法を探り当て記事にすることができました。苦労の過程はコチラに書き残しています。

謝辞
仕事として和田さんと共同作業したのは今回が初めてでした。また私は秀和さんとも初めてで、進め方の勝手が分からない中、最初から最後まで和田さんにフォローして頂き本当に助かりました。そういえば原稿生成システム(原稿をMarkdownで書いてgithubにpushしたらPDFが降ってくる)を構築してくれたのも和田さんでしたね。これが無かったらきっとスムーズに進まなかったでしょう。何から何まで感謝です!

最後に
書籍タイトルに「iPhoneプログラミング」ってありますけど、iPadにも対応してます!あとアプリ内課金に興味があっていろいろ聞きたいという方がいらっしゃいましたら、直近は関西の勉強会でアプリ内課金のお話をする予定ですので、関西圏にお住まいの方はぜひそちらにいらしてください。
【第3回】がりっち勉強会 in 関西
それでは!

Tagged , , ,

iOS 7/In-App Purchase/ローカルレシート検証

これの続きです。
iOS 7 + Auto-renewable + TransactionReceipt

これで有効期限が取れたのは取れたのですが(値も正しかった)、これで良いのかわかりません。素直にOpenSSL使って、ローカルレシート確認にする方がいいのかしら…

2013/11/4 追記:
このやり方はAppleのドキュメントに書いてないからダメっぽい。
OpenSSL使ってローカルレシート認証にするのが正解だと思われる。
引き続き検証中。

元の話は、iOSのアプリ内課金(In-App Purchase)でレシートの確認を行う場合、SKPaymentTransaction#transactionReceiptが、iOS 7からDeprecatedになっており、さてどうしたらよいのやら…ということでした。そしてこれに対する回答は、Appleのドキュメントに”超”概要が書いてありました。

レシート検証 プログラミングガイド

P.5
レシートをローカルで検証する
ローカルでの検証には、PKCS #7署名を読み込んで検証するコードと署名されたペイロードを解析し て検証するコードが必要です。

P.7
iOSで(システムが古いために)appStoreReceiptURLメソッドが使用できない場合は、App Storeを使用してSKPaymentTransactionオブジェクトのtransactionReceiptプロパティを 検証するためにフォールバックすることができます。

ふむふむ…「appStoreReceiptURLメソッドが使用できるのならレシートをローカルで検証しなさい」というように読み取れます。何故なら「フォールバックする」=「SKPaymentTransactionオブジェクトのtransactionReceiptプロパティを使う」=「Deprecated」だからです。

ではレシートをローカルで検証するにはどうするか…ドキュメントを読むと次のようにありました。
・ローカルのレシートは、Appleの証明書に署名されたPKCS #7コンテナである
・PKCS #7を読みほどいていくにはOpenSSLを使えば出来るけど、ヒントはドキュメントに書いておく
・まぁそんな感じで実装は開発者にまかせた、頑張れ

お、おう…頑張って実装を…無理や!と思ったら、ここら辺をやってくれている方がいらっしゃいました。ファンタスティック!次の3つを使っていけば、レシートをローカルで検証することが出来ます。

・x2on / OpenSSL-for-iPhone
https://github.com/x2on/OpenSSL-for-iPhone

・rmaddy / VerifyStoreReceiptiOS
https://github.com/rmaddy/VerifyStoreReceiptiOS

・Apple Root Certification Authoirty
http://www.apple.com/certificateauthority/

以下手順。
==========
1. OpenSSL-for-iPhoneの中に build-libssl.sh があるので、ターミナルから叩いて暫く待てば出来上がりです。これを使ったビルドにarが必要なのですが、自分環境にarがなかったので

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar /Applications/Xcode.app/Contents/Developer/usr/bin/ar

こんな感じでシンボリックを張ったら通りました。

2. 1で作ったOpenSSLのlibrary2つ、VerifyStoreReceiptiOSの.hと.m、AppleのROOTサーバ証明書(Apple Inc. Root Certificateの方)を、レシートを検証するアプリのプロジェクトに突っ込みます。AppleのROOTサーバ証明書だけプロジェクトのルートに配置してください。他は任意の場所でOKです。それからプロジェクトの設定について、OpenSSLのヘッダが読み取れるようにSearchPathを足すのと、OpenSSLのライブラリをStatic Linkするように変更してください。

3. VerifyStoreReceipt.m の以下行をアンコメント、さらに自分のアプリに合わせて変更してください。

// const NSString * global_bundleVersion = @"1.0.2";
// const NSString * global_bundleIdentifier = @"com.example.SampleApp";

4.  レシートの検証ロジックを、VerifyStoreReceipt.mを使ったものに変更してください。

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
BOOL result = verifyReceiptAtPath([receiptURL path]);
if (result == YES) {
	NSDictionary *dict = dictionaryWithAppStoreReceipt([receiptURL path]);
}

VerifyStoreReceipt.mは、ローカルレシートがAppleによって署名された “正しい” “改ざんされてない” レシートであることを確認し、さらにペイロードの解析結果をNSDictionaryに詰めて返戻してくれます。素晴らしい。

5. これでローカルのレシートを検証すると、NSDictionary *dictに検証結果が次のように入ってきます。

BundleIdentifier = "<Application Bundle ID>";
BundleIdentifierData = <0c2b7075 72636861 73652e61 6e642e61 64766572 74697369 6e672e53 696e676c 65507572 63686173 65497465 6d>;
Hash = <373fcddd 17a4283a 45b9d7e3 00f2e3ed 45553ac2>;
InApp =     (
            {
        CancelDate = "";
        OriginalPurchaseDate = "2013-10-31T16:13:18Z";
        OriginalTransactionIdentifier = 1000000091908178;
        ProductIdentifier = AutoRenewableProduct;
        PurchaseDate = "2013-11-03T19:35:34Z";
        Quantity = 1;
        SubExpDate = "2013-10-31T16:20:05Z";
        TransactionIdentifier = 1000000091908725;
        WebItemId = 18446744072207895811;
    },
(略)
            {
        CancelDate = "";
        OriginalPurchaseDate = "2013-11-03T19:35:33Z";
        OriginalTransactionIdentifier = 1000000092114639;
        ProductIdentifier = AutoRenewableProduct7;
        PurchaseDate = "2013-11-03T19:35:34Z";
        Quantity = 1;
        SubExpDate = "2013-11-03T19:38:33Z";
        TransactionIdentifier = 1000000092114639;
        WebItemId = 18446744072207895811;
    }
);
OpaqueValue = <6f36aadf d0b10b5a d7b08c39 913a7e14>;
OrigVer = "";
Version = "1.0";
}

アプリのBundle Identifierに紐づくレシートが”InApp”に配列で入ってきます。そして有効期限は”SubExpDate”です。配列中のSubExpDateの最大値が、Auto-renewableの有効期限になります。ここで1つ注意があります。1つのアプリで複数のプロダクトを販売している場合、レシートが混在する可能性があります。この場合、ProductIdentifierの値を見て判断しなければなりません。

NSArray *inAppArray = [dict objectForKey:@"InApp"];
NSTimeInterval expires;
for (NSDictionary *inApp in inAppArray) {
	NSString *expiresDate = [inApp objectForKey:@"SubExpDate"];
	NSTimeInterval e = [self dateFromRFC3339String:expiresDate];
	if (expires < e) {
		expires = e; // SubExpDateの最大値
	}
}

==========

実際に動かしてみて正しい有効期限を取得できました。ちなみに dateFromRFC3339String:expiresDate は、RFC3339の日時文字列をNSTimeIntervalに変換するメソッドです。

さて、レシートをローカルで検証する際、問題になるのは、App Store側でAuto-renewableされた購入については、ローカルにレシートが無いということです。これについては、アプリ側でレシートをもらいにいく必要があります。レシートを貰うために、SKReceiptRefreshRequestを使用します。

receiptRequest = [[SKReceiptRefreshRequest alloc] init];
receiptRequest.delegate = self;
[receiptRequest start];

SKReceiptRefreshRequestの応答は、SKRequestDelegateを介して受け取ることができます。

@interface ViewController : UIViewController <SKRequestDelegate>

SKRequestDelegateのrequestDidFinishの中で、先ほどのローカルレシートを取得する処理とSubExpDateの最大値を取る処理を動かせばOKです。

- (void)requestDidFinish:(SKRequest *)request {
  // 上記の4と5をここにも実装
}

(参考)
・x2on / OpenSSL-for-iPhone
https://github.com/x2on/OpenSSL-for-iPhone
・rmaddy / VerifyStoreReceiptiOS
https://github.com/rmaddy/VerifyStoreReceiptiOS

Tagged ,

iOS 7 + Auto-renewable + TransactionReceipt

iOS 7のIn-App Purchase(アプリ内課金)で、Auto-renewable Product(自動更新購読型)の話です。

Auto-renewableなプロダクトが購入されたとき、それの有効期間を確認するために
paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
からレシート情報を取得しApp Storeへ送ると、次のようなJSONが返ってきます。

{
    "latest_receipt" = <latest_receipt_infoのBASE64値>;
    "latest_receipt_info" =     {
        bid = "APPID";
        bvrs = "1.0";
        "expires_date" = 1383368349000;
        "expires_date_formatted" = "2013-11-02 04:59:09 Etc/GMT";
        "expires_date_formatted_pst" = "2013-11-01 21:59:09 America/Los_Angeles";
        "item_id" = 734219217;
        "original_purchase_date" = "2013-10-31 16:10:06 Etc/GMT";
        "original_purchase_date_ms" = 1383235806000;
        "original_purchase_date_pst" = "2013-10-31 09:10:06 America/Los_Angeles";
        "original_transaction_id" = 1000000091908178;
        "product_id" = AutoRenewableProduct;
        "purchase_date" = "2013-11-02 04:54:09 Etc/GMT";
        "purchase_date_ms" = 1383368049000;
        "purchase_date_pst" = "2013-11-01 21:54:09 America/Los_Angeles";
        quantity = 1;
        "transaction_id" = 1000000092068781;
        "unique_identifier" = e9d5856de1f8bd7d99c0137f11d244e32031dee7;
        "web_order_line_item_id" = 1000000027522624;
    };
    receipt =     {
        bid = "APPID";
        bvrs = "1.0";
        "expires_date" = 1383367749000;
        "expires_date_formatted" = "2013-11-02 04:49:09 Etc/GMT";
        "expires_date_formatted_pst" = "2013-11-01 21:49:09 America/Los_Angeles";
        "item_id" = 734219217;
        "original_purchase_date" = "2013-10-31 16:10:06 Etc/GMT";
        "original_purchase_date_ms" = 1383235806000;
        "original_purchase_date_pst" = "2013-10-31 09:10:06 America/Los_Angeles";
        "original_transaction_id" = 1000000091908178;
        "product_id" = AutoRenewableProduct;
        "purchase_date" = "2013-11-02 04:44:09 Etc/GMT";
        "purchase_date_ms" = 1383367449000;
        "purchase_date_pst" = "2013-11-01 21:44:09 America/Los_Angeles";
        quantity = 1;
        "transaction_id" = 1000000092068715;
        "unique_identifier" = e9d5856de1f8bd7d99c0137f11d244e32031dee7;
        "web_order_line_item_id" = 1000000027522621;
    };
    status = 0;
}

そして戻ってきたJSONの latest_receipt_info から expires_date を取得し有効期間とする…というのは、Auto-renewable Productを扱われた方ならよくご存知だと思います。

さて、SKPaymentTransactionのtransactionReceiptはiOS 7からDeprecatedになっています。そして公式ドキュメントには次のものを使いなさいとあります。

NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];

早速それを使い、取得したレシート情報をBASE64符号化し…

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
            {
                // NSString *base64receipt = [transaction.transactionReceipt base64EncodedStringWithOptions:0];
                NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
                NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
                NSData *base64data = [receiptData base64EncodedDataWithOptions:0];
                NSString *base64receipt = [[NSString alloc] initWithData:base64data encoding:NSUTF8StringEncoding];
                int status = [self verifyReceipt:base64receipt]; // レシートの確認処理

これをApp Storeに送ってみたら、こんなJSONが返ってきました。

{
    environment = Sandbox;
    "latest_receipt" = "<latest_receipt_infoのBASE64値>";
    "latest_receipt_info" =     (
                {
            "expires_date" = "2013-10-31 16:15:05 Etc/GMT";
            "expires_date_ms" = 1383236105000;
            "expires_date_pst" = "2013-10-31 09:15:05 America/Los_Angeles";
            "original_purchase_date" = "2013-10-31 16:10:06 Etc/GMT";
            "original_purchase_date_ms" = 1383235806000;
            "original_purchase_date_pst" = "2013-10-31 09:10:06 America/Los_Angeles";
            "original_transaction_id" = 1000000091908178;
            "product_id" = AutoRenewableProduct;
            "purchase_date" = "2013-11-02 04:56:08 Etc/GMT";
            "purchase_date_ms" = 1383368168196;
            "purchase_date_pst" = "2013-11-01 21:56:08 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000091908178;
            "web_order_line_item_id" = 1000000027517734;
        },
(中略)
                {
            "expires_date" = "2013-11-02 04:59:09 Etc/GMT";
            "expires_date_ms" = 1383368349000;
            "expires_date_pst" = "2013-11-01 21:59:09 America/Los_Angeles";
            "original_purchase_date" = "2013-11-02 04:52:41 Etc/GMT";
            "original_purchase_date_ms" = 1383367961000;
            "original_purchase_date_pst" = "2013-11-01 21:52:41 America/Los_Angeles";
            "original_transaction_id" = 1000000091908178;
            "product_id" = AutoRenewableProduct;
            "purchase_date" = "2013-11-02 04:56:08 Etc/GMT";
            "purchase_date_ms" = 1383368168196;
            "purchase_date_pst" = "2013-11-01 21:56:08 America/Los_Angeles";
            quantity = 1;
            "transaction_id" = 1000000092068781;
            "web_order_line_item_id" = 1000000027522624;
        }
    );
    receipt =     {
        "adam_id" = 0;
        "application_version" = "1.0";
        "bundle_id" = "APPID";
        "download_id" = 0;
        "in_app" =         (
                        {
                "expires_date" = "2013-10-31 16:20:05 Etc/GMT";
                "expires_date_ms" = 1383236405000;
                "expires_date_pst" = "2013-10-31 09:20:05 America/Los_Angeles";
                "is_trial_period" = false;
                "original_purchase_date" = "2013-10-31 16:13:18 Etc/GMT";
                "original_purchase_date_ms" = 1383235998000;
                "original_purchase_date_pst" = "2013-10-31 09:13:18 America/Los_Angeles";
                "original_transaction_id" = 1000000091908178;
                "product_id" = AutoRenewableProduct;
                "purchase_date" = "2013-11-02 04:53:25 Etc/GMT";
                "purchase_date_ms" = 1383368005000;
                "purchase_date_pst" = "2013-11-01 21:53:25 America/Los_Angeles";
                quantity = 1;
                "transaction_id" = 1000000091908725;
                "web_order_line_item_id" = 1000000027517733;
            },
(中略) 
                        {
                "expires_date" = "2013-11-02 04:59:09 Etc/GMT";
                "expires_date_ms" = 1383368349000;
                "expires_date_pst" = "2013-11-01 21:59:09 America/Los_Angeles";
                "is_trial_period" = false;
                "original_purchase_date" = "2013-11-02 04:52:41 Etc/GMT";
                "original_purchase_date_ms" = 1383367961000;
                "original_purchase_date_pst" = "2013-11-01 21:52:41 America/Los_Angeles";
                "original_transaction_id" = 1000000091908178;
                "product_id" = AutoRenewableProduct;
                "purchase_date" = "2013-11-02 04:53:25 Etc/GMT";
                "purchase_date_ms" = 1383368005000;
                "purchase_date_pst" = "2013-11-01 21:53:25 America/Los_Angeles";
                quantity = 1;
                "transaction_id" = 1000000092068781;
                "web_order_line_item_id" = 1000000027522624;
            }
        );
        "receipt_type" = ProductionSandbox;
        "request_date" = "2013-11-02 04:56:08 Etc/GMT";
        "request_date_ms" = 1383368168224;
        "request_date_pst" = "2013-11-01 21:56:08 America/Los_Angeles";
    };
    status = 0;
}

どーん。latest_receipt_infoという名前なのに、過去のトランザクションが配列で全部付いてきてるじゃないですかー。これではlatestの意味がないのでは…おまけにstatusが常時0になっていて、statusで有効期限の判定が出来なくなっています。

というわけで、この配列をぐるぐる回しつつ、有効期限(expires_date_ms)の最大値を取得してみました(#Linq欲しい)。

        // latest_receipt_info配列をまわして、最大の有効期限値を取得する
        NSArray *receiptArray = [dict objectForKey:@"latest_receipt_info"];
        NSNumber *expiresDateMax = [[NSNumber alloc] init];
        for (NSDictionary *receiptDict in receiptArray)
        {
            NSNumber *expiresDate = [receiptDict objectForKey:@"expires_date_ms"];
            if ([expiresDateMax doubleValue] < [expiresDate doubleValue])
            {
                expiresDateMax = expiresDate;
            }
        }
 
        // 有効期限が切れていないか判定する
        NSTimeInterval expires = [expiresDateMax doubleValue] / 1000.0;
        if (expires < [[NSDate date] timeIntervalSince1970])
        {
            // 有効期限切れだよ!
            return 21006;
        }

これで有効期限が取れたのは取れたのですが(値も正しかった)、これで良いのかわかりません。素直にOpenSSL使って、ローカルレシート確認にする方がいいのかしら…

2013/11/4 追記:
このやり方はAppleのドキュメントに書いてないからダメっぽい。
OpenSSL使ってローカルレシート検証するのが正解だと思われる。
引き続き検証中。

2013/11/9 追記:
ローカルレシート検証出来ました。
iOS 7/In-App Purchase/ローカルレシート検証

Tagged ,

tumblr. ステッカーをもらいました

tumblr.こんなWeb Clipしてたりするわけですが、ふとtumblr.さんのステッカーが欲しいなーと思い、入手してみることにしました。以下のようにAirMailで「ステッカーください!」って手紙を出す感じです。

入手方法

  1. 国際返信切手券を購入する(\150-)
    ステッカーを送り返してもらうための切手になります。郵便局で売ってます。「国際返信切手券ください」と言えばOKです。
    日本郵便 – 国際返信切手券
  2. 手紙を書く
    「tumblr.好きです、ステッカーください!」的なのを英文で書きます。英文レターのマナーはよく知らないのですが、文面はWordで、サインは自筆で書きました。
  3. 送信用・返信用の封筒を作る
    送信用・返信用の封筒にそれぞれ「送付先」「送り元」の住所・名称を記載します。もちろん、送信用と返信用で書く場所が入れ替わりますのでご注意を。それとAirMailの書き方は適当にググってくださいw tumblr社の住所は、tumblrのaboutページに記載されているものを書きました。
  4. 手紙に封をする
    送信用の封筒に「国際返信切手券」「手紙」そして「返信用封筒」の3つを入れて封をします。
  5. 手紙を送る
    郵便局へ行って「AirMail送りたいのですが」と言えばOKです。おそらく\120-で送ることができます。

自分の場合、送ってから3週間ちょっとで返信が届きました。ステッカーは20枚くらい入ってました!
tumblr.ステッカー

そしてMacBook Airに貼ったらこんな感じです。カッケー!
tumblr.ステッカー

tumblr.さん、ありがとうございました!

Tagged

Android-Binding Before / After

プログラミング生放送 名古屋 第24回で登壇してきました。
お題は「Android-Binding Before/After」ってことで、Androidネタです。
内容難しい…って言われました、セッションネタ間違えた感(

あのBGMを脳内で再生しながら見てくださいw

Tagged

MacBook AirにmicroSDXC 64GBを装着

これ(SanDisk UHS-I対応 microSDXCカード 64GB SDSDQY-064G-U46A)を
SanDisk MicroSDXC 64GB

これ(GREEN HOUSE microSD/SDHC/SDXC USBカードリーダ GH-CRMR-MMW)に
GREEN HOUSE microSDXC USBカードリーダ

こうして
microSDXC USBカードリーダにmicroSDXCを突っ込む図

こうしたら
microSDXC USBカードリーダをMacBook Airに装着

こうなりました。
microSDXC 64GBを認識

容量の少ないMacBook AirのSSDを少しでも空けるためにこんなことをしてみました。これに入れているのはiTunesの楽曲データ30GB弱です。楽曲データはファイルI/Oが頻繁になく、また高速ファイルアクセスは不要(SSDに置く必要がなく)、コンパクトに持ち歩きたいという自分のニーズに対し、たどり着いた答えがこれでした。

この手の(USBコネクタ部分にmicroSDカードを内蔵する)コンパクトなカードリーダーでmicroSDXC規格に対応しているのは、2012年8月でGREEN HOUSEのコレだけのようでした。手元のBUFFALOの同種製品は32GB(つまりmicroSDHC)までしか認識できませんでしたので、ここは気をつけないとですね。

ちなみにファイルR/W速度は、すっごく…………遅いですorz カードリーダーがSDXC UHS-I高速転送に非対応かつUSB2.0というのがボトルネックなのでしょう。とはいえiTunesデータの利用に支障ない程度なので、今はとても満足しています。

MacBook Air 11-inch (Mid 2012) を購入

MacBook Air 11′ (Mid 2012)を購入しましたので記念写真をアップ。

買い替え前
・MacBook Pro 17′ (Mid 2009) Core2Duo 2.8GHz / 500GB HDD / 8GB Mem / 1920×1200
買い替え後
・MacBook Air 11′ (Mid 2012) Core i7 2.0GHz (Turbo Boost時3.2GHz) / 256GB SSD / 8GB Mem / 1366×768

  1. 届いたところ。外箱はいつもの通りですね。オーダー日が書かれたシールが貼ってあります。発表のあった6/14にポチり、届いたのは6/20でした。
    MacBook Air梱包箱
  2. 箱から化粧箱を取り出しました。無駄に大きくないサイズです。
    MacBook Airの化粧箱
  3. 化粧箱を開けてご対面です。「このMacBook Airはあなたに出会うために生まれてきました」…なんてTextはありませんでしたが、そういうメッセージは受け取りました!蓋の裏にもクッション材が貼ってあって、丁寧なところが伺えます。
    MacBook Airとご対面
  4. 化粧箱の下半分です。MagSafe2アダプタ、電源コードが入っています。
    MagSafe2アダプターなど
  5. MacBook ProのMagSafeアダプター(上)とMagSafe2アダプター(下)のコネクタ部分。MagSafe2アダプターはコネクタが薄く幅広いです。MagSafaとMagSafe2
  6. 箱から取り出したMacBook Air 11′。ふ、ふつくしい…
    MacBook Air 11' (Mid 2012)MacBook Air 11' (Mid 2012)
  7. キーボードはUS配列です。スッキリしていて、ふ、ふつくs(ry
    MacBook Air 11' (Mid 2012) USキーボード
  8. MacBook Air 11′とMacBook Pro 17′を比較。親子亀状態。Airの小ささというかProのデカさがよくわかりますw
    MacBook Air 11'とMacBook Pro 17'
    横から見るとAirの薄さがよくわかりますね!
    MacBook Air 11'とMacBook Pro 17'

これで電車駆動開発が進むぞ!

楽しいアプリ制作の会#13でMVVMをしゃべってきた

たのアプ#13を開催し無事に終了してきました。

今回は「はじめてのMVVM」というセッションをやりました。
・MVVM概要
・MVVMで重要なViewModelの役割
・バインディング
・MVVM Light Toolkit
というポイントを説明してきました。
バインディングの利点は説明だけだと分かりにくいのではないかと思い、擬人化デモ(寸劇)しながら
説明したところ割と評判よかったですw

資料はこちらです。

わんくま同盟 大阪勉強会 #47でしゃべってきました

2/25に大阪で開催された「わんくま同盟 大阪勉強会 #47」でセッションしてきました。「HTML5ネタを何かやってよ!」ってお願いをされていたのですが、そこは海のように広いHTML5の世界。50分のセッションでは収拾がつかないと思い、今回もSencha Touchを担いでいきました。資料は以下です。

2ヶ月ぶりのセッションで喋りの感覚が鈍ってました(汗 しゃべる内容の順番が一部前後したりして聞き苦しかったところがあると思います。すみません。。。次はもっと分かりやすく伝えれるように頑張ります。

そういえば、、、お昼休みの直前に急遽LTセッションの依頼があり、LT資料を即席で作って喋りましたb 「楽しいアプリ制作の会」の宣伝です。

こっちは笑いを取れたので満足ですw