XCode 3でActionScript

XCode3 + AS3

家のMacのOSをLeopardにしたら、それにあわせてXcode 3にバージョンアップしてた。そのせいで、以前は快適に動作していたXCodeでのActionScriptのプログラミング環境が使えなくなってなってしまった。XCodeでのActionScriptプログラミングに慣れてしまうと、Emacsキーバインドは完璧に使えるし、コード補完も快適なので手放せないものになっていただけに、かなり困る。仕方ないのでLeopardにADCからダウンロードしたXCode2.5を追加インストールしてAS作業はそっちでやっていたりしたのだが、いまいち釈然としない。

「XCode3 AS3」などでググってみたのだがなかなかそのあたりの記述がみつからず一時あきらめかけていたのだが、ようやく発見。(XCode3+Flexで検索すればよかったのか…)

このエントリーの末尾にリンクしてあるファイル一式をダウンロードして以下の場所にコピーする。ちなみに設定ファイルの場所はXcode 3から変更された。

/Developer/Library/Xcode/Specifications/

Xcode用のActionScript 3.0テンプレートは、シン石丸さん作成のテンプレートがそのまま使用できる。こちらもダウンロードしてインストール。こちらもXCode 3から場所が変更されているので注意。

/Developer/Library/Xcode/Specifications/Project Templates/

これで、XCode 3でも快適にActionScriptを編集できるようになった。コードの折り畳み機能、かなりイイ感じ。TextMate派の方々はこちらへの乗り換えを検討してもいいのではないでしょうか。なんといっても無償で提供されているし。

MacBook Pro

つい先日マイナーチェンジした、MacBook Proを買ってしまった。My first Intel Mac。これで、Adobe CS3系のアプリの操作がサクサクと動くようになるといいな。速いマシンを買って、仕事も速くなるといいんだけど…なかなかそうならないのが不思議。

ついでに、Time Capsuleも買ってみた。散々溜めこんだ音楽データや、過去の仕事のアーカイブなどは、全てこっちに移行してしまいたい。

Flash(AS3)でcrossdomain.xml無しにクロスドメインにアクセスする

たまにはFlashの技術メモ。常識なのかもしれないが、昨晩ちょっとひっかかった部分なので、備忘録的に…

Flashの仕様でやっかいなのは、ドメインをまたいで外部の情報を読み込む場合、参照側のサーバーに、カスタムポリシーファイルcrossdomain.xmlがないといけない。独自に参照側も作成する場合はcrossdomain.xmlを作成すれば問題ないのだけれど(詳細はこちら→Flashヘルプ - ドメイン間のデータロード許可)、外部のWebAPIを利用する際にはcrossdomain.xmlを設置していないサービスも多く、ちょっとやっかいな問題。

昨晩は、GoogleMapsのジオコーディングの情報をFlashに読み込む必要があったのだが、GoogleMapsのサーバにはcrossdomain.xmlがないためそのまま素直にはAS3でAPIを叩いても情報を取得できない。ということで解決策を調べてみた。

参考にしたのは、下記のサイト。この例ではYouTubeのvideos of the dayのAPIを例にしている。

ここで採用している方法は、PHPでProxyを作成して、それを経由して情報を取得するという方法。つまり、PHPからだとcrossdomain.xml無しに情報にアクセスできるので、AS3からはローカルに置いたPHPを参照して、これを経由してGoogleなどのサイトにアクセスするということ。

まずは、Proxyとなる、crossdomain-proxy.phpを用意する。

<?php
$post_data = $HTTP_RAW_POST_DATA;
$header[] = "Content-type: text/xml";
$header[] = "Content-length: ".strlen($post_data);
preg_match("/url=(.*)/",$_SERVER['REQUEST_URI'],$params);
$ch = curl_init( $params[1] ); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
if ( strlen($post_data)>0 ){
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
$response = curl_exec($ch);     
if (curl_errno($ch)) {
    print curl_error($ch);
} else {
    curl_close($ch);
    header("Content-type: text/xml; Content-length: ".strlen($response));
    print $response;
}
?>

これを、最終的にデプロイするswfファイルと同じドメインの任意の場所にアップロードする。このPHPをAS3から呼び出すのはこんな感じ。

var GOOGLE_ID:String = "[GoogleAPIのデベロッパID]";
var _crossdomain_proxy:String = "[アップロードしたPHPファイルのパス]"+"crossdomain-proxy.php?url=";
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGeocoderrorHandler);
xmlLoader.addEventListener(Event.COMPLETE, onGeocodeHandleComplete);
xmlLoader.dataFormat = URLLoaderDataFormat.TEXT;
xmlLoader.load(new URLRequest(_crossdomain_proxy + "http://maps.google.com/maps/geo?q="+"[サーチするテキスト]"+"&output=csv&key="+GOOGLE_ID));

簡単なサンプルも添付しておきます。例えばこれとModestMapsなどを組み合わせれば、ジオコードをFlash上のマップとリンクできるわけです。ここらへんの詳細はまたの機会に。

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.IOErrorEvent;
    import flash.net.*;
    import flash.xml.*;
    import fl.controls.Button;
    import fl.controls.Label;
    import fl.controls.TextInput;
    import flash.text.TextField;
    import flash.text.TextFormat;
    
    public class CrossdomainSample extends Sprite 
    {
	private static var GOOGLE_ID:String = "[Google APIのデベロッパID]";
	private var _crossdomain_proxy:String = "http://yoppa.org/works/crossdomain_sample/crossdomain-proxy.php?url=";
	private var _ti:TextInput;
	private var _searchBtn:Button;
	private var _status:TextField;
	
        public function CrossdomainSample(){
	    _ti = new TextInput();
	    _ti.move(20,20);
	    _ti.setSize(240,20);
	    addChild(_ti);
	    
	    _searchBtn = new Button();
            _searchBtn.move(262,20);
            _searchBtn.label = "search";
	    _searchBtn.setSize(80,20);
	    _searchBtn.addEventListener(MouseEvent.CLICK, onButtonClick);
	    addChild(_searchBtn);
	    
	    _status = new TextField();
            _status.defaultTextFormat = new TextFormat("Verdana", 12, 0x000000);
	    _status.border = true;
	    _status.x = 20;
	    _status.y = 50;
	    _status.width = 320;
            _status.height = 60;
	    addChild(_status);
	}
	
	//Geocodeボタンがクリックされた際の処理
        private function onButtonClick(event:Event):void 
        {
	    //プロクシーのPHPを介して、Geocodeにアクセス
	    if(_ti.text != ""){
		var xmlLoader:URLLoader = new URLLoader();
		xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onGeocoderrorHandler);
		xmlLoader.addEventListener(Event.COMPLETE, onGeocodeHandleComplete);
		xmlLoader.dataFormat = URLLoaderDataFormat.TEXT;
		xmlLoader.load(new URLRequest(_crossdomain_proxy + "http://maps.google.com/maps/geo?q="+encodeURI(_ti.text)+"&output=csv&key="+GOOGLE_ID));			
	    }
        }
	
	//IOエラー
	private function onGeocoderrorHandler(event:Event):void {
	    _status.text = "データ読み込みエラー";
	}
	
	//GeoCode読み込み完了
	private function onGeocodeHandleComplete(event:Event):void {
	    trace(event.target.data);
	    var loc:Array = event.target.data.split(",");
	    _status.text = "緯度: "+loc[2]+", 経度:"+loc[3];
	    if(loc[2] == 0 && loc[3] == 0){
		_status.text = "そのような場所は存在しません";
	    }
	}
    }
}

しつこくミニチュアシリーズ

ミニ汐留

汐留にて撮影。やっぱり高度が高いほうがそれらしくなるみたいだ。

UHA @大門

その帰りに大門でUHA味覚糖のビルに遭遇。看板がオシャレ。

箱庭雪景色

ミニチュア冬景色

週末、久しぶりに風邪を引いて寝込んでしまった。ひとつ仕事に区切りがついがので油断したのかもしれない。寒空の下、はしごして飲んだせいという気もするが…。

丸一日おとなしく家で寝て、日曜日だいぶ快復したのでカーテンを明けて外を見てびっくり、一面の雪景色。都内でここまで雪が積もったのは、久しぶりのような気がする。

窓からの雪景色を、またもやミニチュア化してみた。