iOS 向けのユニットテスト環境にひき続き関心があり。ある程度の知識がたまってきたので整理した。
今現在iOS 向け開発でモックやマッチャーも含めて、ユニットテストを行なおうとするならだいたい使うのはこんなパターン
GHUnit 以外はOCUnitサポートが入っていて、内部的にSenTestingKitのテストケースを実行しているのでXcode上からCommand-U で実行できる。CI 用のコマンドラインからの実行や、JUnit 互換のレポート出力などには今回は触れない。
モック・マッチャーフレームワーク自体の話はまた別の機会にするとして、サイトだけリストアップしておきます。
なので保守的に安定して使えるフレームワークであると言える。以下にアップルの公式ドキュメント翻訳がある
ちなみに、個人的には過去にiTunes App Store に申請したバイナリにSenTestingKit.framework をリンクしてしまいアップルレビュワーにリジェクトをされるという経験を何回かしており、憎しみを抱いている。
使ってる開発者の数(シェア)だと
SenTestingKit > GHUnit > Cedar = Kiwi > Specta
ぐらいだけど、成熟したWeb開発に比べてそもそもTDD/BDD自体がそんなにやっている人がいない……
それを踏まえて「こういう人にはこれがおすすめ」というのを記します。
"A -> B -> C "というのは試してみる順番です。
Cedar, Kiwi, Specta のBDDスタイルあたりだとシェアはしばらくCedar とKiwi が横並び状態ではあったんだけど、開発の勢い(あいまいだ)では個人的には最近——といっても1ヶ月ぐらいはKiwi がちょっと抜けてきた印象。
Kiwi を取り扱った電子書籍も出てる(US向けiBooksストア)
ただ自分が使ってるうちではなんかの拍子で突然テストランナーぶっ壊れたり(テスト成功するけど、テストコード走ってないとか)してターゲット再作成したり。非同期処理でうまくいかなかったりもした。
他にGoogle のCocoa 向け開発チームが提供しているGTMUnitTest とかもあったんだけ、これはSenTestingKit がまだiOS向けでに標準サポートされていない時に使いやすくする目的でものなので、最近はチェックしていないし。新たに使い出す人もあんまりいなそう。
あと初期iPhone3G時代に速攻でリリースされたことが印象に残っている内国産のiUnitTest も開発が続いている。
BDDスタイルのものは、ここで3つあげたけど。ただ、RSpec の恍惚が忘れられないRuby プログラマ兼iOSアプリ開発者たちによって日々新たなツールが生産し続けられているので将来的にはどれが流行っているかは正直わからない。
最新Xcodeで /Developer 以下がクリーンされたけど
Dashcode とか見当らない……
で、ここにある
NSURL* url = [NSURL URLWithString:@"http://example.com/api/hi.json"]; NSString* fakeResponse = @"{\"title\": \"Hello, 世界!\"}"; [URLRaider order:url body:fakeResponse]; NSURLRequest* request = [NSURLRequest requestWithURL:url]; NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; NSString* responseText = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; STAssertEqualObjects(responseText, fakeResponse, nil); STAssertEqualObjects(responseData, [fakeResponse dataUsingEncoding:NSUTF8StringEncoding], nil); [URLRaider purge];
URLRaider:order:body でURL とレスポンスのテキストを指定するだけ。
URLRaider:purge でオーダーしたルールを忘れる
使いどころとしては
など
git clone https://github.com/laiso/URLRaider.git
とくに依存するフレームワークとかないので、URLRaider/URLRaider/ ディレクトリ以下のクラスファイルをそのままプロジェクトにインポートするのがいいでしょう(あっ、そう言えばすっかりARC側の住人になっていたのでARC無効のプロジェクトでどうなるか試していない)。
サンプルアプリが以下にあります。サードパーティのライブラリなどを利用しているので最初にgit-submodule で取得してください。
git submodule update --init
open Examples/Examples.xcodeproj
method swizzling 的なことをしているわけでもない。Cocoa 標準のNSURLProtocol の拡張でリクエストを乗っ取ってメモリ上に登録したテキストに置き換えているだけ。なのでURLリクエスト(http://, https://, file://, ftp:// ? 全部確認したわけではない) の動的置き換えに特化した。
「モックライブラリで代用できないの?」と最初は思ってて、OCMock とかCocoa の世界にもあるんだけど、そもそもモックとかスタブとかスパイとかフェイクとか依存性の注入とか。この辺のテスト用語周辺がよくわかんなくて、自分が思う「テスト用にHTTPリクエストの結果を置き換え」という最小の問題解決がなかなかできずにいたので(これは定義でいうとスタブの範疇っぽい)、必要最小限のものを作ってみた感じ。
抽象化されたモックオブジェクトベースのものと比べると、ランタイムの低レベルの部分なのでObjective-C ベースのいろんなサードパーティのライブラリにも効果が適用されることとかが良さげ。プロジェクトのExamples アプリケーションでは。
で動作確認をしている(https://github.com/laiso/URLRaider/tree/master/Examples 参照) 。AFNetworking はー、忘れてた。あとでやってみます。
ただ、ほぼ自分用なので実験的なモジュール扱いだと思う。iOS アプリケーションのテストでしたまだ試していない。
ロードマップ として考えられるのは例えば
ぐらい
他のプログラミング言語環境の似たような動作するモジュールないかなと探してた過程。この手のものはFakeリクエストと呼ばれるみたい。ただ今回のはCocoa のNSURLProtocol で置き換えているだけで、リクエスト自体は本物だよなー(結果は偽物だけど)と思ったので採用しなかった
今までは苦し紛れにホスト部分を動的に "user:password@example.com" などに書き換えていた。そんな日々ともお別れです。
サンプルコードは以下。2とうりの方法で実装した→5パターンに増えた。
https://github.com/laiso/iOSSamples/tree/master/UIWebViewAuthentication
だいたい以下のdelegate で実装している。ちなみにパスワード間違えてる時などUIWebViewDelegate のwebViewDidFinishLoadl: で受け取れない仕様なのがアレである(タイムアウトで対応?)。
// UIWebViewAuthentication/DetailViewController.m #pragma mark - UIWebViewDelegate - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if([request valueForHTTPHeaderField:@"Authorization"]){ return YES; } NSMutableURLRequest* req = (NSMutableURLRequest*)request; NSString* authToken = [NSString stringWithFormat:@"%@:%@", USER, PASSWORD]; GTMStringEncoding *coder = [GTMStringEncoding rfc4648Base64WebsafeStringEncoding]; [req addValue:[NSString stringWithFormat:@"Basic %@", [coder encodeString:authToken]] forHTTPHeaderField:@"Authorization"]; [webView loadRequest:req]; return NO; }
NSURLCredential で設定しておくことでもいいらしい。
やってみた。ちょっと冗長になったけど以下のよう。UIWebView がアクセスする前にNSURLConnection で対象のサーバへのリクエストを発行して認証関連のdelegate が呼ばれるのでそこでBasic 認証のNSURLCredential をセットする感じ。
// USENSURLCredential/USENSURLCredentialViewController.m - (void)viewDidLoad { [super viewDidLoad]; [self registerMyCredential]; //[self configureView]; } - (void)registerMyCredential { NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:DEFAULT_URL]] delegate:self]; [conn start]; } - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSURLCredential* creds = [NSURLCredential credentialWithUser:USER password:PASSWORD persistence:NSURLCredentialPersistencePermanent]; [[challenge sender] useCredential:creds forAuthenticationChallenge:challenge]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [self configureView]; } - (void)configureView { NSString* url = DEFAULT_URL; [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]]; }
こっちのが元からあるFoundation の仕組みを利用していて自然ではあるが、2回リクエストが飛ぶ?
このNSURLCredential を使った認証ではパスワードなどは自動的にOS内のキーチェーンなどのセキュアストレージに保存されるらしく。認証が成功したらその後はNSURLCredential を発行しなくても閲覧できるようなった。
お、じゃあNSURLCredential を使った方がいいかな? というとおそらくURL にパスワード含ませた時もヘッダいじった時も内部的に同じ処理をしていると思われる。のでどれがいいのかはよくわからず。
この件 stackoverflow.com とかでも既出ではないみたいなんで見掛けたら回答しておくつもり。
NSURLCredentialStorageを直接使えるかもという情報を教えてもらったので確認している。
あとNSURLCredentialPersistencePermanent ってやっていることから、サンドボックス外に認証情報が保存されているなら一端iOSを初期化しないと確認できないね。
iOS初期化(シミュレータでいう'Reset Content and Setting')めんどくさいので、起動直後にこうやった。
// clean NSURLCredentialStorage* store = [NSURLCredentialStorage sharedCredentialStorage]; [[store allCredentials] enumerateKeysAndObjectsUsingBlock:^(NSURLProtectionSpace* space, NSDictionary* credHash, BOOL *stop) { NSURLCredential* cred = [credHash objectForKey:USER]; [store removeCredential:cred forProtectionSpace:space]; }];
(というかよく見たら先のAkimbo App Studio のエントリに書いてあった……)
こうやってみた。が結果これは認証成功しない。
修正したらできた。url.port がnil なのに気付いていなかったようだ
- (void)setCredential { NSURLCredential* creds = [NSURLCredential credentialWithUser:USER password:PASSWORD persistence:NSURLCredentialPersistenceForSession]; NSURLCredentialStorage* store = [NSURLCredentialStorage sharedCredentialStorage]; NSURL* url = [NSURL URLWithString:DEFAULT_URL]; NSURLProtectionSpace* protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:url.host port:80 protocol:url.scheme realm:@"Input ID and Password." authenticationMethod:NSURLAuthenticationMethodDefault]; [store setCredential:creds forProtectionSpace:protectionSpace]; }
これは先に言われてるとうりREALM の一致が必須だった。REALM 知らないという場合、Xcode環境あるなら以下のコマンドで調べられる
curl -Iv 'http://www.chama.ne.jp/htaccess_sample/index.htm' ... # WWW-Authenticate: Basic realm="Input ID and Password." ...
レスポンスヘッダの"Input ID and Password." の部分がそれ。
デバッグの過程で気付いたんだけど、一番目のUIWebView のリクエスを書き換えパターンで認証通した場合 [store allCredentials] に出てこない……
どこか別の場所で管理しているのか。とりあえずわかった事実は以下
なぞが多い……。”URLCredentialStorageに直接保存” ができれば一番いいんだけど。→できた
もう一個わかった
つまりはUIWebView でBASIC 認証を通した時の保存先がなんかおかしい。ということ以上わからず。
iOS向けアプリ開発者がハマるの諸悪の根源はUIWebView の実装であり。業界の不況もすべてはUIWebView の実装のせいだと思っています。
最近乱立している「モバイルアプリのバックエンド(Web API)に特化したサービス」に興味があったので、いろいろ試してみた。
「モバイルアプリのバックエンド(Web API)に特化したサービス」と書いたけど、まだまだそんなに一般的ではないのでBaaS(Backend as a Service)とかCloud Hosting とかいろんな呼ばれ方をしている && サービスによって提供する領域も違う。まあとくに定義はしないので気になったらその都度公式のヘルプでも読んでみて。本稿では呼びやすいのでBaaS と表記します。
最初に後程も紹介するdonayama さんのCocoafish の紹介エントリの一節がわかりやすいと思ったので引用します
おそらくどのようなサービスであっても「ユーザ管理」をすると思いますが、ひとことにユーザ管理といっても、(場合によっては招待状による招待→)サインイン→ユーザ作成→ログイン処理/ユーザ情報メンテ/パスワードリマインダ…といった機能を実装する必要があります。
画面UIももちろんですが、その辺のバックエンドとなるサーバ側のAPIとデータベースの処理や維持管理も必要になってきますよね。
こういったバックエンドで行う「いつものこと」や「最近ならやらないとね」といった機能をサービスとして提供しちゃおうというのがSaaSの一種にあたるBackend as a Service(BaaS)と呼ばれるWebサービスで、Cocoafishもそのひとつにあたります
アフター・アイフォンというか、以前はPCウェブブラウザ向けのサイトが一番優先度が高かったので、Webまわりのサイトやシステムを構成する流れとしてはこんな感じが主流だった。
が、最近はクライアントサイドの連携を考えて最初からWeb API を作ってしまうケースも結構多い。iPhone のみのサポートでスタートしたInstagram とかが有名な例。その場合は以下のような流れがありがち。
この背景には
などの要因がありそう。あとBackbone.js やKnockout.js などのウェブアプリケーション向けのクライアントサイドベースのMVC/MVVMフレームワークの注目などもこの流れの中にあると思わる(Cross-Origin Resource Sharing の仕組みが一般的になればBaaSの恩恵はある)。
なので、Web API をいかにはやく構成するかで製品の基盤になり、その後の展開(別のプラットフォーム向けとか)がしやすくなる。
しかし一人でアプリの実装とWeb APIの構成などをやるのはなかなか大変だし、アプリとWeb API向けに別の技術を学習しないといけなかったりでたいへん。企業が制作しているアプリとかだと、開発者がサーバ側とアプリ側で複数いることも結構ある。
で、これから先この「サーバ側」にあたる仕事がかぶりがちな部分を提供することでアプリ制作の本来の領域に集中できるとかなんとか。
具体的な使いどころとしては例えば
などがある。
保存されたデータを都度取得することにより、ユーザーには「データの同期」という機能が提供できる。
データの永続化についてはiOS(Cocoa)にはCore Data というSQLiteなどをバックエンドにした複雑な処理もこなせる本格的なデータベースシステムがあるんですが、iOS に限っていえばサンドボックスの外に出れなかったり使い方が難しかったりでそこまで受け入れられていませんが、バックエンドと組合せて抽象化することでさらなる可能性が出てきそうです(例えば参照関係が複雑な同期処理など)。
いわゆるリモートからのプッシュ通知=Apple Push Notification Service についてはサーバとapple のサーバ間をSSL 経由で接続しないといけないので。利用できるポートなどが制限されていると実現できない。のでGoogle App Engine や共有レンタルサーバでバックエンドを構成するとと必然的にApple Push Notification Service 経由のプッシュ通知はできなくなる。このへんを踏まえた上でHTTP ベースのAPI だけで開発者サーバ→サービス業者サーバ→Apple サーバ→デバイス という流れでプッシュ通知を実現するサービスがある。
全体的に特定のHTTPリクエストでやりとりすることでJSON/XML形式のデータ取得や保存をする、というREST APIスタイルのサービス(たいていAPI key のような文字列が発行され、それをリクエストにのせる)と、
iOS向けのSDKを用意して内部的なREST API は隠匿されていて、開発者はObjective-C ベースのHTTP クライアントでAPI を呼び出しdelegate メソッドなどで処理することを期待する、という形式がある感じ。
あと、REST API とObjective-C ベースのAPI どちらもオープンにしているところもある。
前者の方が好きなHTTP Client を選べたり、細かいところまで調整できたりしそうだから好みだけど、SDKベースのが使う方は簡単かな?
データストレージ、Push通知、ユーザー管理、位置情報、twitter/Facebook連携など。無料プランもあり。
HTML5/iOS/Android に対応。既存プロジェクト向けと新規プロジェクト向けのSDKがダウンロードできる。
StackMobは今回リストアップした中で一番多機能に見える。多機能というか手広い印象。PCブラウザ向け、Java、Ruby クライアントも用意してある。
あと変った機能として、サーバサイドにアップロードしたJava とScala で書かれたコード片がREST APIから呼び出せる。これによって「APIの結果をほげほげしてアプリに表示」という処理を→「ほげほげした結果を返す」というようなことができそう。パフォーマンス上の利点や、サーバーサイドへ柔軟性を持たせる為の仕組みかな?
SDK 提供形式は漢のソースコード丸ごと。が、UDIDに依存(サーバへ送信)している(どうするの、、)。サンプルコードはなぜかXcode 設定がARCオンになっているのにコードが対応していないのでARC を手動で無効にしないとビルドエラー出てた(ツンデレ設定か)。あとMainStoryboard_iPhone が存在しないのに依存するようになっていて結局サンプルサプリは動作確認できず。
ちなみに、ベータ版をうたっていないサービスはここだけだった。
パプリックベータ期間中のサービス。
データストレージ、Push通知、ユーザー管理、位置情報、twitter/Facebook連携など。無料プランもあり。
Framework 形式のSDK が提供されており、アプリに組み込んでそこからObjective-C ベースの簡単なAPIでデータなどを操作する。ユーザー登録して試してみたところすんなり利用できた。アプリごとにClass -> Object というデータ構造で保存でき、RDM でいうテーブルとカラムのような構造。Object のプロパティに別のClass のObject を設定したり疑似クエリ APIクラスで問い合わせなどできる。名前からのイメージ的にForce.com か。
パプリックベータ期間中のサービス。
ユーザー管理、データストアなど基本的なサービス。REST API ベースで利用する。ベータ期間中は無料らしい。
CloudMine もStackMob のようにサーバサイドのコードが実行できて、こちらはサーバーサイドのJavaScript を実行する仕組みが存在する。
パプリックベータ期間中のサービス。
ユーザー管理などの基本的なサービス。無料プランもあり。
Buddy はシステムを.NET で構成しているらしく、iOS 以外にも Windows Phone7 方面のサポートも期待できそう。
REST 以外にSOAP Interface も用意されていたり、ちょっと癖がある。SDKの提供などはとくにない。
パプリックベータ期間中のサービス。
コンテンツデータをAmazon S3 を利用したストレージに格納できるサービスや、ユーザー管理、位置情報、チャット、メッセージングなどのモジュールがある。無料プランもあり。
SDK がFramework ファイル形式で配布されている(5 Minute Guide - QuickBlox Wiki にサンプルアプリ動作までのチュートリアルがある)。
ユーザーはQuickBlox の独自アカウントを作成するかFacebook アカウントを接続して使用する。
サービス自体は使えるが、管理画面とかはまだ工事中でできてない部分とかあった。
プライベートベータ中のサービス。
サインアップに招待コードが必要。なのでまだ試せていない。*1
先日Titanium Mobile で有名なAppcelerator社に買収されたことで話題になっていたサービス。
ユーザーやフレンド管理からチェックイン、汎用データストアまで、これでもかというぐらいモジュールが充実、細分化されており開発者が必要なものを都度ピックアップしやすくなっている感じの戦略っぽい。
donayama さんのレポートがわかりやすい。
プライベートベータ中のサービス。
登録しておくと準備でき次第通知してくれる。積極的にBaaSという言葉を使っている印象。
まだログインできてないので詳細がわからないが、資料など見ると汎用的なストレージAPIっぽい。通信の暗号化やスケーラビリティなどもうたっていた。
あと、なんかランディングページがダサい(今日日の日・北米的なセンスではない)。
プライベートベータ中のサービス。
タブレットやエンタープラズ用途を推している。まだ開店準備中という感じ。
自分でサーバを用意し、セットアップすることでスモールスタートできるような製品。主にデータストレージ機能の提供に注目した。
http://code.google.com/p/jsonengine/
Google App Engine を利用するApache v2ライセンスのJava製ソフトウェア。
REST API でJSON形式のデータをやりとりする。汎用データストアみたいな感じなので使いやすい。Google App Engine 上に構成するのでスケーラビリティなど、Google App Engine 自体の利点をそのまま生かせる。管理画面とか。
https://github.com/couchbaselabs/CouchCocoa/
CouchDBはApache Software Foundation によるスキーマフリーなドキュメント指向なデータベース。CouchCocoaがCocoa向けのクライアントライブラリ。
CouchDBは本体にブラウザ向けインターフェースなどが組みこまれていたり、HTTP/RESTful JSON API ベースで動作するよう作られているので今回のようなケースには合いそう。
MongoDB もREST インターフェイス機能がついてた。
またホスティングサービスもあった
Push 通知や課金周りの処理を代行してくれる。無料プランもあり、現在は月に100万回の通知を送れる。
WebSocket による リアルタイムメッセージングサービス*2
その他、ゲーム向けのプラットフォームSDKなどにもバックエンドをホスティングしてくれるサービスがある。
今回は詳しくは触れないが、Mobage ngCore SDK などはサーバ/クライアントアーキテクチャの考え方から一歩踏みこんだ意欲的なシステムに見え、おもしろそうではある。
Mobage ngCore SDK
https://developer.mobage.com/ja#mobage-gametech
Pankia
その他に○○とかいいんじゃない?
このアプリはこれを使っているらしいよ、など知っていたら教えてください
モバイルアプリのバックエンドやBaaS について流行ってきてはいるものの、国内では同等サービスをリリースしている事例を知りません。起業家はローカライズするチャンスともいえますが、そもそも国外も含めてこれからどの程度受け入れられるかというのはまだわかりません。サービスの方向性(何を、どの程度提供するかなど)もまだまだ二転三転しそうではあります。
まずは、上記先行した人々のサービスを実際に利用してみて確かめてみるのもよいのではないでしょうか。
全体的にまとめるには早過ぎた印象。
が、今すぐ使うならSDK付き +セットアップ簡単+とりあえずひととうり動作確認できたParse.com か QuickBlox 次点でStackMob がおすすめかな。Titanium Mobile パワーで将来的にはCocoafish も幅を効かせてきそう。
Database .com とかもBaaSに分類できると思う / モバイルアプリのバックエンド(Web API)に特化したサービスのまとめ - laiso - iPhoneアプリ開発グループ URL
Salesforce が提供している Database.com というのも教えてもらった。ざっと見たところREST API ベースでChatter ソーシャルAPI など既存のSalesforce製品の連携なども考えたれているようだった。
エンタープライズを意識しつつも汎用的で、料金体制を見るとある程度無料範囲で使えそう。
http://www.appiaries.com/jp/index.html
いつの間にか国内業者からもサービスが出ていたみたい。現在パブリックベータ期間中。デベロッパー向けの案内は以下
*1:招待枠など提供できる方ください。メールは laiso(at).lai.so
*2:http://b.hatena.ne.jp/tinsep19/20120224#bookmark-82188501
"Reveal Archive in Organizer" が有効になってないとオーガナイザは開かない。
なにかしらの自動化ツールの影響でかわっていたり、自分でかえてしまっていたりする。
# バンドル化
xcodebuild -configuration Release -sdk iphoneos
# パッケージング
/usr/bin/xcrun -verbose -sdk iphoneos PackageApplication `pwd`/build/Release-iphoneos/MyApp.app -o `pwd`/build/MyApp.ipa --sign "iPhone Distribution: Omaeno NAME"
xcodebuild すると build/Release-iphoneos/MyApp.app にできてるのでそれを .ipa にする。
xcrun はラッパーで /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication みたいなパスのコマンドを実行している。
PackageApplication を生で扱うと出力先のディレクトリ全消去(上書き)したりする過激なツールなので、実行前にはバックアップしておく(.svn .git とかも消去される)のをおすすめする。
--sign の引数にはXcode のビルド設定でRelease に使っている証明の名称をそのままコピペしておく(デフォルト指定でうまくいく場合もある)。
他の記事では--embed という引数で証明書も渡しているみたいだけど、普段ビルドに使っているマシンでキーチェインに全部登録してるならつけなくてもよさげ。
でも.ipa のファイルフォーマットってPayload/ 以下に .app がそもまま入っているだけのZIP なのでipa 作りたいだけで署名をやり直す必要ってなさそうなんだけどなー。
bdunagan – Ad-Hoc and App Store IPAs with xcrun
http://www.bdunagan.com/2011/12/12/ad-hoc-and-app-store-ipas-with-xcrun/
iPhoneアプリビルドのシェルスクリプト - 風日記
http://d.hatena.ne.jp/mmasashi/20111120/1321800829
bdunagan – Ad-Hoc and App Store IPAs with xcrun
http://www.bdunagan.com/2011/12/12/ad-hoc-and-app-store-ipas-with-xcrun/