検証機のAndroidが放電して仕事がストップしたので、メモ代わりに書きます。拙作決済用プラグインLiterally WordPressを9.1にアップデートしたのですが、次期アップデート9.2ではiOSのIn App Purchaseの非消耗プロダクトと連携させようと思っています。要するに、角川BOOK☆WALKERみたいなアプリを作る場合のバックエンドとしてWordPressを利用しようという魂胆です。
ただし、このプラグインで実現できるのは、あくまで決済情報とファイルダウンロードの管理だけであって、よいアプリケーションを作るには他にも必要なものが色々とあります。今回はアプリ – サーバ間の通信の基本を説明するとともに、サーバサイドで必要な設定をさわりだけ説明しようと思います。
なおはじめに断っておきますが、ぼくはこの方法を使ったiOSアプリを一つもリリースしたことがないため、大半は机上の空論です。「たぶんできるだろう」ということを書いています。
クライアント(iOSアプリ)とサーバ(WordPress)の通信方法
大きくわけて2つの方法があります。
1. ただのWebサイトとして表示
WordPressにはスマートフォン向けのプラグインやテーマ等が豊富にあるため、スマートフォンで表示しようと思ったら簡単です。iOSアプリにはUIWebViewなどの「ブラウザと同じ機能を果たすクラス」が存在するので、それで表示します。このやり方はいろんなところで紹介されています。
この方法の問題点は「普通のWebブラウザで見たときと何も変わらない」ことです。実際、Appleは「ただのWebサイトをUIWebViewでラップしたもの」を審査で落とすように方針を変えたと思います。詳しくはおググりください。
2. XML-RPCで通信
こっちが本題です。XML-RPCというのはXMLを使った通信の規格ですが、ほぼすべてのプログラミング言語においてライブラリが用意されているので、使う方は特にXMLの構造がどうなっているのかについて気にする必要はありません。iOSから配列を投げるとPHPで配列を受け取れるというような、素敵な仕様になっています。このXMLRPC通信を利用することで、クライアント(iOSアプリ)とWordPressに会話をさせます。
WordPressのXML-RPC
WordPressでは設定 > 投稿設定で「XML-RPC」の欄にチェックを入れると有効になります。エンドポイントは http://exampl.com/xmlrpc.php です。
デフォルトのままで用意されているAPIはCodexのXML-RPC WordPress APIに書いてあります。これを利用してアプリを作ることもできますが、「だったらWordPress for iOSでいいじゃん」という結論になってしまいますので、オリジナルのAPIを作る必要があります。この方法は後述。
クライアントのXML-RPC
クライアント(iOS)のXML-RPCライブラリは WordPress for iOS のクラスをそのまま使うという方法がiPhoneでXMLRPC | TechRachoにて紹介されています。非同期通信がサポートされているみたいですね。
オリジナルのAPIを設計する
さて、元々あるものを作ってもしょうがないので、今回は「デフォルトのXML-RPCの仕様にない機能を作る」ということを目指します。会員制サイトということなので、「会員設定が公開になっているユーザーのリストを取得」ということを目指しましょう。
1. データ構造
SNSのような機能を持ったWordPressを想定してください。「会員設定が公開になっているユーザー」とは「privacy_level
というキーのuser_metaを持っていて、値は0」だと仮定します。一応、ページネーションを付けるので、受け取る引数は数値のみです。返すのは総ユーザー数とユーザー情報からなる配列にしましょう。メールアドレスなどは晒しちゃまずいので、ユーザー名、IDぐらいにしておきましょう。
2. 名前
XML-RPCのAPIはドットシンタックスでつなぐっぽいので、名前空間をmywp
として、このAPIの名前をmywp.users
とします。
3. WordPressにAPI用フックを作成
WordPressは例によってフックを多用しますが、XML-RPCもフックで追加できます。mywp.users
というフックを追加してみましょう。テーマのfunctions.phpにでも書いてください。できる人はプラグイン化するとよいでしょう。
/** * XMLRPCのエンドポイントを追加するフック * @param array $methods * @return array */ function mywp_methods($methods){ $methods['mywp.users'] = 'mywp_users'; return $methods; } add_filter('xmlrpc_methods', 'mywp_methods');
これでmywp.users
をコールするとmywp_users
という関数が呼び出されます。この関数を作成して配列を返すようにすれば、完成です。それでは、ユーザーを取得して返す関数を作ります。
/** * ユーザーを取得する * @global wpdb $wpdb * @param array $args * @return array */ function mywp_users($args){ global $wpdb; //XML-RPCの引数はすべて一つの配列にまとまってきます $paged = isset($args[0]) ? max(1, absint($args[0])) : 1; $per_page = 20; //1ページ20件 $offset = ($paged - 1) * $per_page; $sql = <<<EOS SELECT SQL_CALC_FOUND_ROWS u.ID, u.display_name FROM {$wpdb->users} AS u INNER JOIN {$wpdb->usermeta} AS um ON u.ID = um.user_id AND um.meta_key = 'privacy_level' WHERE CAST(um.meta_value AS UNSIGNED) = 0 ORDER BY u.user_registered DESC LIMIT %d, %d EOS; //データを連想配列で取得 $users = (array)$wpdb->get_results($wpdb->prepare($sql, $offset, $per_page), ARRAY_A); $total = intval($wpdb->get_var("SELECT FOUND_ROWS()")); return array( 'total' => $total, 'users' => $users ); }
プロフィールやアバターのURLなどはちょっと応用すればできると思います。
4. XML-RPCの動作確認
さて、作ったはいいものの、本当に動いているかを確認する必要があります。これを確かめるには、iPhoneアプリでモックを作るのがよいのですが、面倒なのでPythonで検証します。iPhoneアプリを作るということはMac使っているでしょう。Pythonも入っていると思います。おもむろにターミナルを立ち上げてpythonと入力してください。Pythonインタプリタが立ち上がります。
#ライブラリをインポート import xmlrpclib #サーバに接続 api = xmlrpclib.Server('http://example.com/xmlrpc.php') #実行 api.mywp.users(1) # {'total': 1, 'users': [{'display_name': u'\u9ad8\u6a4b\u6587\u6a39', 'ID': '1'}]}
終わったらCtrl + Dで終了です。importでこけた場合はxmlrpclibが入っていないので、「python インストール xmlrpclib」でおググりください。たしかデフォルトで入っていたと思います。
このように、Pythonという別クライアントからWordPressの情報が取得できました。Objective-Cでも事情はだいたい同じだと思います。
補足1 文字列や配列以外のデータ
文字列、数値、配列などを渡す場合はこんな感じですが、画像などのバイナリデータを直接渡したい場合は専用の関数が用意されています。WordPressのXML-RPCはThe Incutio XML-RPC Library for PHPを拡張したものなので、これを使います。
$image_data = file_get_contents($path_to_img); return array( 'name' => basename($path_to_img), 'data' => new IXR_Base64($image_data) );
クライアント側ではBase64デコードして使いましょう。一緒にmimeタイプとかファイルハッシュを渡すといいかもですね。
補足2 ログイン
XML-RPC自体にはログインにあたる機能はありません。ただ、会員専用サービスであれば、ログイン機能を持たせたいでしょう。もちろん、Webサイトと共有できればなおよしです。
この場合、アプリ側でユーザー名とパスワードを保存しておき、毎回サーバ側に投げるという仕様になります。これはかなり微妙な仕様ですが、iOSアプリであればサンドボックスで守られているのでギリギリセーフでしょうか。これ以上なんとかしたかったら、OAuthプロバイダになるしかないですかね。
さて、XML-RPCでユーザー名とパスワードを受け取って認証させるには、こんな風にさせるとよいです。mywp.getProfileでmywp_get_profileが呼び出されるようにしたとしましょう。
/** * ユーザーのプロフィールを取得する * @param array $args */ function mywp_get_profile($args){ global $wp_xmlrpc_server; //IXRクラスの拡張 $user_name = $wp_xmlrpc_server->escape($args[0]); $pass = $wp_xmlrpc_server->escape($args[1]); if(!$user = $wp_xmlrpc_server->login($user_name, $pass)){ return $wp_xmlrpc_server->error; } //これ以降はユーザーが設定されているので、 //WordPressのユーザー関数が使える $user_id = get_current_user_id(); }
認証の部分は別関数でラップしておくとよいかもしれません。
以上、だいたい感じは掴めたでしょうか。今回は机上の空論を長々と展開しましたが、LWPのStoreKit対応はたぶん行うので、ご期待ください。
おまけ WordPressでiOSアプリのバックエンドを作るということ
Instagramは写真共有サービスとして大ブレイクしましたが、スタートアップ当初はWebサイトに全然力を入れていませんでした。いまもWebサイトはショボいですね。
そうしたトレンドを踏まえると、はたして今回紹介したような方法でiOSアプリのバックエンドを作るのが正解なのかどうかは正直微妙です。スキルセット次第でしょうか。
iOSアプリはエコシステムとして大きいので、StoreKitのバックエンド専用Saasとかが存在すると思います。アドホックアプリ配信サービスのTestFlightとかもあるぐらいですからね。アプリによってはWebサイトの存在する価値が薄かったりもするので、今回紹介したのは昨今流行のリーンスタートアップ的な手法とは真逆を行くやり方です。