ActionScript 3.0 , AIR , FLEXからCocoa, OpenGL
25 Feb
以前、名古屋のFxugにて少し発表した内容なんですが、ずっと記事にできてなかったので、今更ながらまとめます。
密かに前から社内用発表会のネタとして作ってはいたのですが、僕個人的にも、なかなか披露する機会がないまま、時間が過ぎてしまいました。
概要としては、
1. iPhone上でボタンをタップすると、AIR 2で作ったサーバーに、iPhoneからSocket経由で信号を送ります。
2. AIR 2のサーバーが、信号を受け取り、信号に応じた処理を行います。
これが作ったアプリのキャプチャです。
iPhoneとAIR 2サーバーの関係は、この通りです。
簡単に行ってしまえば、これだけです。
では、ここから説明します。
1. AIR 2のサーバー部分を作成
AIRでサーバーを作るには、UDPとTCPのサーバーがありますが、今回はTCPで通信するので、flash.net.ServerSocketクラスを使用します。
宣言〜受付開始
<pre>import flash.net.ServerSocket; // 接続してきたSocketを保持する var socks:Vector. = new Vector.(); // インスタンス生成 var server:ServerSocket = new ServerSocket(); // 接続時のイベントハンドラ作成 server.addEventListener(ServerSocketConnectEvent.CONNECT, serverSocketConnectHandler, false, 0, false); // 待ち受けポートを指定(43243番で受け付ける) server.bind(43243); // 待ち受け開始 server.listenen();</pre>
接続時:
function serverSocketConnectHandler(event:ServerSocketConnectEvent):void
{
// 接続してきたSocket
var socket:Socket = event.socket;
// 接続してきたSocketにデータを返すため、イベント設定
socket.addEventListener(ProgressEvent.SOCKET_DATA, receiveHandler, false, 0, false);
// 接続してきたSocketを保持する
socks.push(socket);
}
受信時:(本来は宛先を指定するべきだが、特に指定せず、全てのSocketに送信しています)
function receiveHandler(event:ProgressEvent):void
{
// データを送信したSocketを取得
var socket:Socket = event.target as Socket;
if(socket!=null) // nullチェック
{
// 受信データを取得
var recv:String = socket.readUTFBytes(socket.bytesAvailable);
// 操作対象のVideoPlayerを取得
var target:VideoPlayer = getCurrentVideo(); // 自作のメソッドです
// コマンドを取得し、操作を実行。コマンドを取得するため、indexOfを使っています。
if( recv.indexOf("prev") >= 0) {
setPos("prev");
} else if( recv.indexOf("next") >= 0) {
setPos("next");
} else if( recv.indexOf("play") >= 0) {
if( target.playing ) {
target.pause();
} else {
target.play();
}
}
}
}
AIRで作るサーバー周りは、こんなところです。エラー処理や切断時の処理などは書いてなくてごめんなさい。
iPhone側アプリケーションのSocket処理
では、次。iPhoneのほう。これも簡単な書き方でごめんなさいね。と、いうかCで最低限の実装しかしていないです。
TCPSendClient.m
接続:
-(void)connect:(NSString *)ip toPort:(NSInteger)port
{
if(_connected) // TCPSendClientが持っている、接続状態を保持する変数
{
return;
}
_connected = NO;
// IPアドレスを取得
servIP = [ip UTF8String];
// portを取得
servPort = port;
// socketが正しく生成できているかチェック
if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
NSLog(@"socket creation error.");
return;
}
// socketの設定
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(servIP);
servAddr.sin_port = htons(servPort);
// 接続
if(connect(sock, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
{
NSLog(@"connect error.");
return;
}
_connected = YES;
NSLog(@"connect succeed.");
}
送信:
-(void)send:(NSString *)message
{
mes = [message UTF8String];
mesLen = strlen(mes);
// 送信
if(send(sock, mes, mesLen, 0) != mesLen) {
NSLog(@"send failed.");
}
}
ちなみに、
TCPSendClientクラスは、Socket処理を受け持つクラスで、自作しています。
TCPSendClient.hはこんな風に書いています。
#import <Foundation/Foundation.h>
#import <stdio.h>
#import <sys/socket.h>
#import <arpa/inet.h>
#import <stdlib.h>
#import <string.h>
#import <unistd.h>
@interface TCPSendClient : NSObject {
int sock;
struct sockaddr_in servAddr;
unsigned short servPort;
const char *servIP;
const char *mes;
unsigned int mesLen;
BOOL _connected;
}
-(void)connect:(NSString *)ip toPort:(NSInteger)port;
-(void)send:(NSString *)message;
-(void)close;
-(BOOL)getConnected;
@property(readonly) BOOL connected;
@end
このサンプルでは、このようにTCPSendClientを使っています。
PagerViewController.m
-(IBAction) processConnect:(id)sender // ボタンをタップした時のAction
{
if([connectSwitch isOn]) // これは、接続のon/offを切り替えるUISwitchです。
{
[sendc connect:@"10.0.2.1" toPort:43243]; // sendcというのがTCPSendClientです
stateField.text = @"connect";
}
else
{
[sendc close]; // 本文では紹介しなかったですが、切断します。
stateField.text = @"disconnect";
}
}
データ送信:
-(IBAction) btnUp:(id)sender // 各ボタンのTouchUpInsideと関連づけています。
{
isPress = NO;
prevValue = 0;
if( (UIButton*)sender == btn) // btnとは、一つ前の動画にフォーカスをあてるためのボタンです。
{
dir = @"prev";
[sendc send:dir];
}
else if((UIButton*)sender == nextBtn) { // nextBtnとは、一つ次の動画にフォーカスをあてるためのボタンです。
dir = @"next";
[sendc send:dir];
}
else if((UIButton*)sender == playBtn) { // playBtnとは、フォーカスがあたっている動画を再生するためのボタンです。
dir = @"play";
[sendc send:dir];
}
}
ちなみに、こちらがiPhoneアプリの画面です。(すごい地味ですね。。。笑えない)
Interface Builderのキャプチャです。
Macをルーターにする
最後に、iPhoneとAIRサーバーを接続するための設定をします。Macなら「インターネット共有」を使えば簡単にルーターにして連携させる事が可能です。こういうときに、一つのメーカーが作っている事が良い事に思えますね。
今回は、AirMac経由で共有します。
1. 「システム環境設定」を開く
2. 「共有」を開く
3. 「インターネット共有」を選択する
4. 「共有する接続回路」がAirMac以外であることを確認。なっていなければ変更します。
→iPhoneとMacをAirMac(無線)で接続する
5. 「インターネット共有」のチェックを入れると、共有を開始するか確認してきますので、開始をクリックします。すると、画面右のAirmacのアイコンがこのようになります。
開始順としては、AIRサーバーとiPhoneが接続する必要があるので、
1. Macをルーターにする
2. AIRサーバーアプリを起動する
3. iPhoneアプリを起動して接続する。
4. iPhone上で操作する。
というところです。
今回簡単に紹介しすぎててすみません。このままでわかりにくいなどご意見ありましたら整理した上で、ソース込みで全て公開します。
22 Jul
現在作っているアプリでは、iPhoneからファイルとデータをアップロードする仕組みが必要になり調べてみたのですが、なかなか目的を達成してくれるクラスやライブラリが見つからなかったので自分で作ってみました。
ソースとテストで使ったPHPのファイルもgithubのここにおいています。
http://wiki.github.com/mmlemon/HTTPMultipartPost
ソースにはcopyrightとか書いていますが、改編などはご自由にどうぞ。ただし、こちらではいかなる責任も負いませんのでご了承下さい。
内容は、こんな感じです。
MultipartPostHelper:このクラスを使ってファイルのアップロードを行います。
実際の使い方は、HTTPPostSample2ViewController.mをご確認下さい。
使用する流れは下記の通りです:
以上です。
13 Jul
つい先日、iPhoneに新しい機種、iPhone 3GSが発売されました。
なので早速僕も購入して、現在開発中のアプリを実機に入れて試そうとしたときにちょっと上手くいかなかったのでメモしておきます。
プロジェクトファイルを開いて、Device – 3.0 | DebugとしてiPhone 3GSに繋ごうとすると、
とか言われてしまい、実機にアプリをインストールすることができなかった。
当然と言えば当然の話しなんですが、provisioningファイル作るときに、DevicesでIDを登録してるんでした。この辺に関しては、iTunes経由で古いiPhoneからうまい具合に調整してくれていると思ってたのですが、そんなこと無かったです。考えてみれば
ということなので当然なのですが。
Deviceに対応していないprovisioningファイルを使おうとしても使えないのは当然です。なので、iPhone Developer Programで新しい機種のIDを登録し、Provisioningを修正して新しい機種にも対応するProvisioningファイルを再生成し、インストールすると、無事に実機にアプリケーションをインストールすることができるようになりました!めでたしめでたし。
追伸:
こんなところでつまづく僕ですが、ほそぼそと開発合宿なるものをやっていますので、ご興味あれば参加表明していただければと思います。
7 Jul
今作っているアプリで時間経過を表示する必要がありましたので、サンプルを作成しました。
ほとんどこちらのサイト様を参考にしています。ありがとうございます。
http://cocoa.synck.jp/1237172145.html
一式はgithubにおきましたので、必要でしたらこちらからどうぞ。
http://github.com/mmlemon/iPhoneTimerSample/tree/master
時間を測る事は、
開始時刻をNSTimeIntervalで保持します。
現在時刻を保持する方法は、
NSTimeInterval time = [NSDate timeIntervalSinceReferenceDate];
を使うだけです。
二つのNSTimeIntervalを比較するには、単純に引き算するだけです。
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate] – time;
(※timeは計測開始時刻を保持するNSTimeInterval)
定期的に表示時刻をアップデートするにはNSTimerを使います。こんな感じ。
self.timer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
scheduledTimerWithTImeIntervalがタイマーが呼ばれる期間であり、
selectorがこのscheduledTimerWithTimeIntervalで定期的に呼ばれるセレクタ(メソッド?)である、という事を押さえておけばよいと思います。
ちなみに、selectorで呼ばれるものは、
-(void)updateTimer:(NSTimer *)t
のようになる必要があります。
ここで経過時刻を測定、表示すれば継続的に表示が更新されるようになります。
タイマーを終了する場合は、
[self.timer invalidate];
として下さい。
追記1:
開始時刻と累積時刻が二重に存在しているので上手くないですね。。
追記2:
github初めて使ったんですけど、つまらないところでひっかかっちゃいましたが、それはまた別記事にします。
追記3:
こんな程度の内容ですが、記事を書く事で改めて気づく事がありました。
これからも続けようと思います。
1 May
結構前になってしまうのですが、4/18-4/19の2日間、湯河原にてiPhoneアプリ開発合宿#2を行いました。
メンバーは、kara_dさん、gnueさん、allnightappsさん、kirara_397さんと僕(mmlemon)の5名で、幹事はkara_dさんでした。
宿泊先近辺はこんな感じです。

宿泊地の岩本旅館。とても風情のあるところでした。


今回は当然ですが、ネット(e-mobile)が使える環境でした。
今回も各自自分の作りたいアプリを作るという形式で、実際にアプリを作られて操作感などをテストされていた方、ウェブと連携させるアプリを作られている方で、サーバー側のロジックを作られた方、またはiPhone OS 3.0の機能について技術調査されていた方、これから作ろうとするアプリのコンセプトを考えるかた、と本当に一人一人がいろんな事をやっていて日頃一人で開発している時はあまり意識していなかったところに気づかされることもあり、大変有意義な時間を過ごす事ができました。
僕はというと、最近どはまりしていたところがあったのですが、allnightappsさん、gnueさんの力を借りて簡単に解決できました。
やっぱりこうやって同じところに集まって合宿することは明日からも頑張ろう!という良い刺激にもなるし、合宿の日までに或る程度形にしないといけない!と自分を追い込むこともできるので、これからも続けたいな、と思いました。
で、月も変わった事ですので、第三回の予定を立てようと思います。
http://123.writeboard.com/8464cc38ec43064f5/v/show/6745677
pass: ipdc09
です。
ご興味もたれた方はtwitterのアカウントなどご連絡先などを添えて追記して下さい。
あるいは、twitterで@mmlemonまでDMいただければと思います。
18 Feb
さて、先日2009.02.14-02.15の間、自分のちょっとしたつぶやきから始まった『iPhoneアプリ開発合宿』に行ってきました。
場所は箱根、強羅 一の湯。
http://www.ichinoyu.co.jp/gora/
twitter上で呼びかけ、twitter上でやりとりしている方や知り合いの方合計5人で開催されました。
参加者は実際にiPhoneアプリを作っている人だけで、普段はなかなかきくことができないObjective-Cの話をする事ができました。
今回の合宿は、あくまで各自作りたいもの、やりたいことがあって、同じ場所でそれぞれの目標に向かって一日作りましょう、という主旨でした。
それぞれが作った(作ろうとした)アプリについてはどこまで何を書いてよいのかわからないので、大まかに書いておくと、
で、僕はここ最近取り組んでいるOpenALを使ったアプリの開発を続けました。(完成にはたどり着けなかったですが、そこそこ進めることができました)
なんと『インターネットが使えない』という問題があったものの、豊富な経験とバックグラウンドを持っているメンバーが集まったおかげで、各自メインテーマが違っていたり、ネットが使えなかったりしながらもみんなで協力しながら開発を続けることができ、いい刺激になりました。
特に、appleのサンプルソースがいかに優れているかという事を実感しました。
また、チェックイン15:00のチェックアウト12:00であることもあり、夜中まで頑張っていました。
途中で温泉につかったり、飲める人はお酒を呑んで開発を続けたり。意外とお酒を呑んでもみんな開発を続けていたのがとても印象的でした。
何より、みんなで集まってiPhoneアプリ作りができる、という環境が今まで無かったのでとても濃く、充実した時間を過ごす事ができました。
これからiPhone合宿が色々と開催されたり、この会も第二回目が開催されますが、iPhone開発している人と沢山であって、良い刺激を受けたいと思っています。

夜中の風景。広い部屋に5人なのでそれぞれ自分のスペースで作業してました。
飲酒後ですが意外にもエンジンがかかってました。

合宿 朝の風景
そして朝。Emobileが赤いのが何とも残念ですね。(EMobileが圏外という事です)

箱根登山鉄道
箱根登山鉄道。スイッチバックを堪能しまくりでした。
また次回も開催予定ですので、今回参加された方&興味のあるかたは是非一緒に合宿しましょう!
参加された方へ:初幹事でいろいろと御迷惑おかけしてと思いますがありがとうございました。これからもよろしくお願いします。