2012年3月28日水曜日

iPhone対応Force.comアプリをiPadでも


■iPhoneアプリをiPad対応に■

Force.com iOS SDKのnativeテンプレートは、iPhone / iPad両方に対応しています。

ただ、iPad版はMaster / Detailが前提。しかし私の作ったFourChatterはChatterがソースなもんで必ずしもきっちりMaster / Detailがあるわけではなくテンプレートのままの形態では動かすことができません。というわけで、当初はdisabledしてました。

しかしそのままというのも何だし、このところまったく開発が止まってしまっているのも何なので久しぶりに手をいれてiPad対応にしてみることにしました。


■経緯■

新しくiPad用のStoryboardを作り、プロジェクトに登録。 iPhone用のStoryboardを別ウィンドウで開いておいて、見比べながらviewをぽちぽち置いていく地味な作業を約30分。とりあえず、iPad上でもRoot Viewが動くようになりました。

で、StoryboardにCommentViewを追加したら…見事にハマりました。

…ってか、このブログハマってばっかりだよなorz


■症状■

症状としては、RootViewだけでは動いていたのにCommentViewをStoryboardに追加しただけでRootViewすら出てこなくなりました。SIGABRTです。そこから約2時間、Assembler上をシングルステップで追いかけてみましたが、どーにも見つかりません。念のためiPhoneで試してみると、動きます。 まぁこの場合、動かなかったら泣きますけどね。


■解決■

最終的な原因は、テンプレートのAppDelegate.mにありました。ここで「iPadだったらsplitViewをどうたら」という処理をしています。そうです、そんなものはとっくの昔に消しちまいました。ということで、この辺をコメントアウトして無事動くようになりました。

で…リリースしようと思ったんですが…FourChatterのウリは「iPhoneの向きを変えるだけでさくっとキーワードを切り替えてChatterを検索できる」っていうところにあります。しかし、iPadをぶんぶん回すとバカみたいという問題があります。私のiPad(初代)は回転ロックしちゃってるしね…。

というわけで、やっぱりiPhoneにはiPhone、iPadにはiPadに向いたGUIがあるよなぁ…というのが結論です。

さて、どうしましょう。タブで切り替えるか、任意の個数のキーワードを登録してタップで切り替えられるようにするか…。ただ任意の個数ってことになると「Four」Chatterっていうアプリ名が意味不明になるわな(4方向のfourです)。

--

でも今四十肩が痛くて新しいGUIのあるべき姿、みたいなことが考えられない。

…これを「四十肩ぐらいでw 言い訳にも程があるww」と思ったヒトは、本当の四十肩の怖さを知らないのだよ。寝返り打つたびに目が覚めるし、何かにつまずいてうっかり手を撞こうものならその場にしゃがみ込むほど痛いし。消炎鎮痛剤効かないし。今も何もしてないのに痛いし。

……以下、記事本体よりも長くなりそうなので省略。とにかく痛いのだ。

この記事を、とび職なのに四十肩でも仕事を休まなかった亡父に捧げます。

四十肩には効かないけど便利なので愛用

2012年3月18日日曜日

MySQLとDatabase.com

■予告と違いますが■

以前からぼちぼち書いていたこっちの記事が先に出来上がったので公開します。


■そのまま比較するのはいくら何でもアレですが■

今まで15年ぐらいMySQL使っていたと思うのですが、今日はじめてMySQL Workbenchを使ってみました。例によってマニュアル読まないのでとっつきにくかったけど、馴染んでしまえば楽勝。やっぱローカルサーバはサクサク動いていいですわ。

Salesforceは、というか、やっぱりWebアプリって、どうしてもサクサク感が足りない。日本にデータセンターできてWebでのレスポンスはよくなったものの、それでも「query」っていうレスポンス感ではないように思います。

Amazon RDSなどクラウド上で動いているMySQLサービスなんてのもあるけど、あれはどうなんでしょ。使ったことのある方いらっしゃいます? まぁ素のqueryとユーザ権限などてんこ盛りにかぶさったForce.com APIを同列に比べてはいけませんけども。


■Force.comからmySQLへ■

先日、Force.com APIで動かしていたFlashアプリをMySQLに移行するというお仕事をしました。一応、互換レイヤを作っておいてサクっと移行のはずですが予期せぬデータ変換トラブルが出るのは業界のお約束でございます。「お約束」ではすまない、とても大変なことになったのですが、その辺は長いので省略。関係者の皆様に改めて御礼とお詫びを申し上げる次第でございます。

その経験を元に、FlashでForce.comあるいはmySQLをアクセスしまくる方法、について簡単にまとめてみたいと思います。


■使用ライブラリ■

以下のライブラリが必要です。swcをダウンロードしてAIRプロジェクトのlibsに入れます。



■Connection/AsyncResponder■

割と似てます。当然Force.comではサーバ指定とポート指定は不要でid, passwordを渡してやればつながります。

mySQL:
mysql = new com.maclema.mysql.Connection("<サーバ>", 3306, "<接続名>", "<ユーザ>", "<パスワード>");
mysql.addEventListener("connect", handleConnected);
mysql.addEventListener("ioError" , errorConnected);
mysql.connect();

..

private function handleConnected(e:Event):void {
    trace( "connection success" );
}

..

private function errorConnected(error:Event):void {
    trace( "connection error" );
    trace( e.toString() );
}

Force.com:
var lr:LoginRequest = new LoginRequest();
lr.username = "<force.com id>";
lr.password = "<password><signature>";

lr.callback = new com.salesforce.AsyncResponder(loginHandler, faultHandler);
force.login(lr);

..

private function loginHandler(result:LoginResult, inObject:Object):void {
    trace("login success");
}

..

private function faultHandler(result:Fault):void {
    trace(result.faultcode);
    trace(result.faultstring);
}

何かするごとにAsyncResponderで成功時と失敗時のコールバック関数を渡してやるのも同じです。

Force.com/mySQLを一本化する場合に一つ問題があります。それは、asSQLはmx.rpc.AsyncResponderを使いますが、Force.comは同じ名前のcom.salesforce.AsyncResponderを使うという点。名前が違うだけなら、宣言と生成でフルパス指定すれば済むんですが、困ったことにコールバック関数の引数の数が違います。もちろん1本のソースコードでmySQLとforce.comに対応、なんてことをやらなければ全然問題ないんですけども。


■Fetch■

MySQL:
var st:Statement = mysql.createStatement();
st.executeQuery("SET NAMES 'UTF8'");
st.sql = "SELECT Id, Name FROM SomeTable__c";
var token:MySqlToken = st.executeQuery();
token.addResponder(new mx.rpc.AsyncResponder(queryHandlerSomeTable, fault, token));

..


private function queryHandlerSomeTable(data:Object, token:Object):void {
    var rs:ResultSet;
    rs = ResultSet(data);
    while( rs.next() ) {
        trace(rs.getString("Id"));
        trace(rs.getString("Name"));
    }
}


Force.com:
strQuery = "SELECT Id, Name FROM SomeTable__c";
force.query(strQuery, new com.salesforce.AsyncResponder(queryHandler, faultHandler));


..


private function queryHandlerSomeTable(result:QueryResult):void {
    if (result != null && result.records != null && result.records.length > 0) {
        for each (var item:SObject in result.records) {
            trace(item.Id);
            trace(item.Name);
        }
    }
    if (!result.done) {
        force.queryMore(result.queryLocator, 
                        new com.salesforce.AsyncResponder(queryHandlerSomeTable,
                                                          faultHandler));
    }
}



単純なfetchなら話は簡単でSOQLとSQLもまったく同じになります。問題はリレーションを使う場合です。force.comはオブジェクト型なので  Account.Name って書くだけで取引先名を引っ張ってこれますが(WebObjectsもそうだったなぁと遠い目)、mySQLはJoinで表を結合してAccount.Name には適当なエイリアス名を付けて参照する必要があります。このへん需要があれば詳しく書きますのでコメントください(ない、というオチが寂しい)


■Update/Insert

需要があれば書きます。


■共通の落とし穴■

fetchした結果はどっちもObject型みたいなもんに入って返って来ます。で、どっちもSQL/SOQLのattribute名とObjectから引っ張り出す時のkeyが間違っていると、fetchしたのにnullだったのか、SELECT文に書き忘れてnullだったのか、すぐにはわかりません。大文字と小文字を間違えてもダメです。SELECT文に書いた通りのkeyと完全に一致しないとダメです。Objectにkeyがあるかどうかを確認して、あるはずのkeyがなかったらエラーを出す、というような処理をはさみましょう。

まぁね…当たり前のことなんだけどね…。


■Force.comでのはまりどころ■

Flashとは違ってAIRで使う場合はデフォルトで同期がONになっています。ので、Fetch後に自動的にForce.com APIに対して問い合わせをします。しかし、相手はForce.com、頻繁に問い合わせするもんであっという間に1日のアクセス制限回数を超えてしまいます。ログイン前にでも以下の行を実行してキャッシュ切ってください。

force.doCache = false;


■asSQLの落とし穴■

update文でprimary keyの値を指定しますが…その値の型が間違っていた場合には、「success側のコールバック関数にfailが帰ってくる」という何が何だかわからない現象が起こります。これ気づくのに2時間ぐらいかかりました…だってねぇ…まさかねぇ…。

2012年3月12日月曜日

ご無沙汰しております

サラリーマンが忙しくて、ぜんぜん手が付けられませんでした。

とりあえず「SalesforceエンジニアのためのFlash入門」という需要があるんだか無いんだかまったくわからない連載をスタートさせようと思います。

どうぞよろしくお願いします。