JavascriptでSingletonパターン
Singletonで実装したい要件があったが、ググッっても良いのがなくて本読んでも残念なものしかなかったので自分で実装してみた。ちなみに本は、JavaScriptパターン ―優れたアプリケーションのための作法。
要件
- newできないようにする。
- 同一オブジェクトを返す。
クラス名は本にあったものと同じUniverseにしたが別になんでもok。
最初にテストコード
newできないか
try { new Universe() } catch(e) { console.info("ok", e); }
new したら例外が吐かれるようにする。
実装
/** * Description of this class. * Singleton Class. * * @class Universe */ var Universe = (function() { var _fromGetInstance = false; var _instance; function _construct() { if (_fromGetInstance !== true) { throw new Error("must use the getInstance."); } _fromGetInstance = false; } /** * @method getInstance * @return {Object} */ _construct.getInstance = function() { if (_instance) { return _instance; } _fromGetInstance = true; return _instance = new this(); } return _construct; })(); /** * func1. * * @method func1 */ Universe.prototype.func1 = function() { console.log("called func1"); }
使い方
var universe = Universe.getInstanse(); universe.func1();
まず、privateにしたいプロパティがあったのでクロージャにした。
returnされるfunctionをnewできないようにしたいので、getInstanceメソッドからでなければ呼べないようにしてしまう。判定方法は、変数_fromGetInstanceがtrueなのかfalseかなのかというだけ。getInstanceでは一度生成したら次からはそのインスタンスを返すだけ。phpの実装と似た感じ。あとはprotptypeに実装したいメソッドを書いていけばてつもどおりになる。
クロスドメインでcookie書き込む方法 +クロスブラウザで
あるサイトから別ドメインのクッキーを書き込む。こういうクッキーは、サードパーティクッキーと呼ばれる。FirefoxとChromeはデフォルトでサードパーティクッキーが書き込めるようになっているが、IEとSafariが問題になる。IEはコンパクトポリシーというものを設定すればいけるが、Safariは出来ない。Safariはデフォルトで”知らないとサイトや広告のみCookieをブロック”となっている。でも、GoogleAdsenseとかは書き込めている。なので調べた。
ポリシーの設定 (P3P) (以下IE対策用)
webサイトで個人情報などを取り扱う場合、ブラウザで設定されたポリシー設定とアクセスしているサイトのポリシーがマッチするか確認する仕組みがある。詳しくは参考サイトを参照。
Platform for Privacy Preferences 1.0 (P3P1.0) 仕様書
http://www.iajapan.org/trans2japanese/w3c/rec-p3p-20020416j.html
【絵で分かるキーワード】P3P
http://ascii.jp/elem/000/000/338/338065/
P3Pコンパクトポリシーをコピペするのが流行らないことを祈る
http://bakera.jp/ebi/topic/3594
今回は下記をHTTPヘッダーに加える。
header("P3P: CP='UNI CUR OUR'");
phpでセッションに書き込むサンプル (以下Safari対策用)
<?php session_start(); header("P3P: CP='UNI CUR OUR'"); var_dump($_SESSION); $_SESSION['writetest'] = 'waaaa';
できない方法
iframeでクロスドメインのURLにアクセスする。
セッションを張るために、HTTPヘッダーでSetCookie: PHPSESSION=****が返ってくるがクロスドメインなので書き込めない。
test-cookie-sample.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Cookie write test of Cross Domain.</title> </head> <body> <iframe name="ifr" src="http://sample.com/test-cookie-send.php"></iframe> </body> </html>
できる方法
iframeの中にform送信すると、クロスドメインのクッキーも書き込める。詳細は不明だが、GoogleAdsenseがこのロジックで書き込んでいる(実際はもっと複雑な行程だが)。つまりGoogleAdsenseは、Safariで広告のクッキーを書き込まないとしているにも関わらず書き込んでいるのだ。良い子は真似しないように。
test-cookie-sample.html
<!DOCTYPE html> <html lang="ja" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Cookie write test of Cross Domain.</title> </head> <body> <form id="postform" method="GET" action="http://sample.com/test-cookie-send.php"target="ifr"> <input type="hidden" name="cookiewrite" value="cookiewritevalue" /> </form> <iframe name="ifr"></iframe> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $(function(){ $("#postform").submit(); }); </script> </body> </html>
iPhoneアプリでNode.js
iphoneアプリでnode.jsと通信する。
ネイティブアプリでリアルタイムな通信ができるので幅が広がりそう。
環境はMacOSX10で、今回は全部ローカル作業。
objective-cでWebSocket通信
iPhoneアプリとnode.jsで双方向通信するのは、もちろんWebSocketで行う。node.jsをインストールしたことある人はわかると思うが、WebSocket通信はsocket.IOモジュールで行っているのでクライアントサイド(objective-c)もsocket.IOにしたい。ライブラリはsokect.IO-objcを使う。
インストール
xcode側
socket.IO-objcの依存ライブラリをインポート
- cocoa-websocket
- RegexKitLite
- json-framework
- ASIHTTPRequest
cocoa-websocket
https://github.com/erichocean/cocoa-websocket
git cloneでダウンロードし、以下をプロジェクトにインポート。
AsyncSocket/
SocketIO.h
SocketIO.m
RegexKitLite
http://regexkit.sourceforge.net/RegexKitLite/
tarでダウンロードし、以下をプロジェクトにインポート。
RegexKitLite.h
RegexKitLite.m
あとFrameworksに以下をインポート
libicucore.dylib
json-framework
https://github.com/stig/json-framework/
git cloneでダウンロードし、以下をプロジェクトにインポート。
Classes/
ASIHTTPRequest
http://allseeing-i.com/ASIHTTPRequest/
git cloneでダウンロードし、以下をプロジェクトにインポート。
ここに詳しい説明がある
http://allseeing-i.com/ASIHTTPRequest/Setup-instructions
socket.IO-objcをインポート
https://github.com/pkyeck/socket.IO-objc
git cloneでダウンロードし、以下をプロジェクトにインポート。
SocketIO.h
SocketIO.m
node.js(サーバー側)
node.jsとsocket.ioモジュールのインストール
nodeのインストールはnvmコマンドで行う。
git clone https://github.com/creationix/nvm.git ~/.nvm . ~/.nvm/nvm.sh nvm install v0.4.7 nvm use stable npm install socket.io
サンプル
説明
iPhone側から接続し、接続完了後にnode.js側へobjcイベント発火。(もちろんこのobjcという名前はてきとうにつけたイベント名)
node.js側でobjcイベント受信したら3秒後にiPhone側へメッセージを送信。
xcode側
まずxcodeで空のプロジェクトを作る。
名前はSocketIoSampleとした。
SocketIoSampleViewController.h
SocketIODelegateをデリゲート。
#import#import "SocketIO.h" @interface SocketIoSampleViewController : UIViewController @end
SocketIoSampleViewController.m
viewDidLoadメソッドに下記を追記。
- (void)viewDidLoad { [super viewDidLoad]; // WebSocket接続 SocketIO *socketIO = [[SocketIO alloc] initWithDelegate:self]; [socketIO connectToHost:@"localhost" onPort:3000]; } // WebSocket接続完了した場合 - (void) socketIODidConnect:(SocketIO *)socket { NSLog(@"-- socketIODidConnect()"); // objcイベント発火。(node.js側でon('objc', function() {...})でキャッチ) [socket sendEvent:@"objc" withData:[NSDictionary dictionaryWithObject:@"hoge" forKey:@"Key"]]; NSLog(@"-- sendEvent()"); } // node.jsからWebSocketでメッセージが送られてきた場合 - (void) socketIO:(SocketIO *)socket didReceiveEvent:(SocketIOPacket *)packet { NSLog(@"-- didReceiveMessage() >>> data: %@", packet.data); }
node.js側
conn-objc.js
var io = require('socket.io').listen(3000); var timer = require('timers'); // WebSocket接続時 io.sockets.on('connection', function (socket) { console.log('CONNECTION ok'); // iPhoneからのobjcイベント受信時 socket.on('objc', function (data) { console.log('CATCH EVENT ok', data); // iPhoneからのメッセージ受信3秒後に、node.js側からメッセージ送信 timer.setTimeout(function() { socket.emit('messages', { echo: 'from node.js!' }); console.log('SEND MESSAGE ok'); }, 3000); }); });
node.jsを起動
node conn-objc.js
xcodeでビルド
xcode側のログ
SocketIoSample[19935:b303] Opening ws://localhost:3000/socket.io/1/websocket/222774812681844920 SocketIoSample[19935:b303] Connection opened. SocketIoSample[19935:b303] onData 1:: SocketIoSample[19935:b303] connect SocketIoSample[19935:b303] onConnect() SocketIoSample[19935:b303] -- socketIODidConnect() SocketIoSample[19935:b303] send() SocketIoSample[19935:b303] send() >>> 5:::{"args":{"Key":"hoge"},"name":"objc"} SocketIoSample[19935:b303] -- sendEvent() SocketIoSample[19935:b303] doQueue() >> 0 SocketIoSample[19935:b303] setTimeout() SocketIoSample[19935:b303] onData 5:::{"name":"messages","args":[{"echo":"from node.js!"}]} SocketIoSample[19935:b303] setTimeout() SocketIoSample[19935:b303] event SocketIoSample[19935:b303] -- didReceiveMessage() >>> data: {"name":"messages","args":[{"echo":"from node.js!"}]}
node.js側のログ
info - handshake authorized 222774812681844920 debug - setting request GET /socket.io/1/websocket/222774812681844920 debug - set heartbeat interval for client 222774812681844920 debug - client authorized for debug - websocket writing 1:: CONNECTION ok debug - websocket received data packet 5:::{"args":{"Key":"hoge"},"name":"objc"} CATCH EVENT ok { Key: 'hoge' } debug - websocket writing 5:::{"name":"messages","args":[{"echo":"from node.js!"}]} SEND MESSAGE ok debug - emitting heartbeat for client 222774812681844920 debug - websocket writing 2:: debug - set heartbeat timeout for client 222774812681844920 debug - websocket received data packet 2::
特に問題なく通信できた。
これでリアルタイム操作が可能なアプリ作れますね。(・・`。
さくらVPSでDropbox
個人サーバーのバックアップがめんどうなので、dropboxのアカウント作ってそこに放り込むことにした。
環境はLinux。CentOS5.6(64bit)さくらVPS。
必要なものをインストール
python2.6
sudo yum install python26
dropboxコマンドの準備
ソースをダウンロードし、権限を与えパスが通っているディレクトリへ移動。
cd ~/src wget https://www.dropbox.com/download?dl=packages/dropbox.py chmod u+x dropbox.py vi dropbox.py 一行目、#!/usr/bin/pythonを#!/usr/bin/python26に変更 mv dropbox.py ~/bin/dropbox
dropboxインストール
dropbox start -i To link this computer to a dropbox account, visit the following url: https://www.dropbox.com/cli_link?host_id=39c6****c3d04b***e6&cl=ja
ブラウザでdropboxにログインし、このurlを叩くとアカウントが紐づく。
~/Dropboxが自動で作成され、同期がはじまる。GUIの時と同じ。
なんて簡単!
確認コマンド
cd ~/Dropbox ls -lA ステータス dropbox status ヘルプ dropbox help プロセス ps aux | grep dropbox
試しにディレクトリを作ってみる。
cd ~/Dropbox mkdir test
ブラウザでアクセス
https://www.dropbox.com/home
反映されているか確認。
起動と停止
dropbox start dropbox stop
自動起動
rootで作業。
サービスファイル作成。
変数のuserとexecの値は各々設定する。
vi /etc/init.d/dropbox
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
権限の付与
chmod u+x /etc/init.d/dropbox
自動起動に登録
chkconfig --add dropbox chkconfig dropbox on chkconfig --list dropbox dropbox 0:off 1:off 2:on 3:on 4:on 5:on 6:off
これで再起動しても自動で起動する。
gitリポジトリの作り方
gitリポジトリの作り方まとめ。
マスターリポジトリ(公開リポジトリ)の作成
mkdir -p /var/git/sample.git cd /var/git/sample.git git --bare init
マスターリポジトリは--bareをつけてinitする。
/home/user/sampleにあるソースをマスターにするためコミット
cd /home/user/sample git init git add . git commit -m 'import'
push先のリポジトリパスを指定し、push
git remote add origin file://var/git/sample.git git push origin master
追加したリポジトリパスを見る場合。
git remote -v
pushしたら、cloneでもってくる。
cd /var/www/vhosts git clone file:///var/git/sample.git sample
最後のsampleはディレクトリ名になる。
iPhoneプッシュ通知まとめ
2011/11/14 : 追記
moruguさんに指摘頂いてdeviceTokenのPOST処理を追記しました。
連絡手段がなかったのでここにお礼として書いておきます。どもです! m(_ _)m
iPhoneアプリ開発でプッシュ通知を使ったので、まとめ。
仕組み
1、APNsにPush通知許可の登録する。
2、APNsからデバイストークンが帰ってくる。
3、そのデバイストークンをサーバー(これは自分で用意します)に送ってDBなりに保存する。
4、サーバーからAPNsにPush通知依頼を出す。
5、APNsは登録済みの指定デバイスにPush通知を出す。
6、受け取ってAlert出すなり色々する。
実装前に準備
Push通知を行うには、iOSDeveloperCenterでPush通知用の証明書をインストールしたりしなければならない。
このフェーズは自分ではやってないので省略orz
参考サイトだけ載せておく。
実装
ここのメソッドは全てプロジェクトのdelegateクラス。
1、APNsにPush通知許可の登録する。
registerForRemoteNotificationTypesを叩くと『プッシュ送信を許可しますか?』のアラートが出て、APNsにリクエストが送られ登録される。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window.rootViewController = self.navigationController; [self.window makeKeyAndVisible]; [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge| UIRemoteNotificationTypeSound| UIRemoteNotificationTypeAlert)]; return YES; }
2、APNsからデバイストークンが帰ってくる。
レスポンスが返ってくると、didRegisterForRemoteNotificationsWithDeviceTokenメソッドがコールされる。
引数にデバイストークンがついてくる。
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)devToken{ NSLog(@"Success : Regist device token to APNS. (%@)", devToken); [self postDeviceToken:deviceTokenString]; // POST }
APNSへのデバイス登録失敗時
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)err{ NSLog(@"Error : Fail Regist to APNS. (%@)", err); }
3、そのデバイストークンをサーバー(これは自分で用意します)に送ってDBなりに保存する。
- (void)postDeviceToken:(NSString *)deviceToken { // prepare url. NSString* content = [NSString stringWithFormat:@"deviceToken=%@", deviceToken]; NSURL* url = [NSURL URLWithString:@"http://192.168.1.5:8080/registDeviceServelt"]; // create instance. NSMutableURLRequest* urlRequest = [[NSMutableURLRequest alloc]initWithURL:url]; [urlRequest setHTTPMethod:@"POST"]; [urlRequest setHTTPBody:[content dataUsingEncoding:NSUTF8StringEncoding]]; // post. NSURLResponse* response; NSError* error = nil; NSData* result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error]; if(error) NSLog(@"error = %@", error); // get result. NSString* resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; NSLog(@"%@", resultString); // release. [resultString release]; [urlRequest release]; }
サーバー側でdeviceTokenを受け取り、DBに保存します。
4、サーバーからAPNsにPush通知依頼を出す。
PHPでやる場合
php-apnsというライブラリを使う。
使う前に証明書を作ったり、ルート証明書をダウンロードしないといけない。ここにやり方が載ってる。
何も考えずにコマンドを叩く^^;
Javaでやる場合
java-apnsというライブラリを使う。
証明書は、キーチェーンからPush通知用にインストールした証明書をエクスポートして使う。
サンプルコード
ApnsService service = APNS.newService() .withCert("/Users/*******.p12", "*****") .withSandboxDestination() .build(); String payload = APNS.newPayload().alertBody("Can't be simpler than this!").build(); String token = "638d09063fe6a73a6cb4*******************ab084e5ea8b0b390d1480f"; service.push(token, payload);
5、APNsは登録済みの指定デバイスにPush通知を出す。
ここはAPNsが勝手にやります。
6、受け取ってAlert出すなり色々する。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { for (id key in userInfo) { NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]); } }
まとめ
Push通知の仕組み自体がシンプルな上に、ライブラリも各種言語あるので簡単。
やっぱりappleはスゴイ。
iPhoneDeveloperに法人登録したまとめ
iPhone開発用に会社のアカウントを取得したときのメモです。
まず初めに
会社で法人として登録する場合、印鑑証明書か登記簿謄本のどっちか必要なので用意が必要です。
さらに審査?に時間がかかるので、今回の場合だと3週間ほどかかりました。
個人登録と同様に年間10800円かかります。
登録の流れ
流れ等は、以下のサイトを参考にして登録しました。とても参考になります。
- http://www.amunsnet.com/blog/iphone開発/317.html
- http://www.amunsnet.com/blog/iphone開発/330.html
- http://www.amunsnet.com/blog/iphone開発/343.html
困ったポイント
Documentation Requested for Your Program Enrollment
このようなタイトルでメールがきました。
登録のためにドキュメントが欲しいらしいのです。
が、何を送れというのが書いてありません。信じられない。。。。
しかたなくサポートにメールして聞いたところ、
申請書と印鑑証明書か登記簿謄本のどっちかを送れということでした。
申請書は、こんな感じで書式はお願いしますとご丁寧に送ってくれましたので、ワードで作って印刷しfaxしました。
faxが届くと、ちゃんと届いたと確認のメールが来るので安心です。
以下はサポートからのメールです。最初からこれ送ってこいや。
この度は iOS Developer Programへお申し込みいただき、ありがとうございます。このEメールは登録手続きに関するご案内です。 Appleでは、Program へお申し込みいただく企業について、実在確認をさせていただいております。 誠にお手数ではございますが、以下『計2点』の書類の写しをご用意いただき、FAXにて弊社までご送付ください。 1)本 Program への申請について、申請者が御社の代表取締役により権限が与えられている事を示す書面(御社本部の所在地と代表電話番号の記載、代表取締役の押印が必要です)1通。文面に関しましては、以下の例をご参照ください。 ---------- 申請証明書 ○○会社(日本語名称および英語名称)代表取締役である <代表取締役の氏名> は、Apple Developer Program の申請に関する一切について、 当社の社員である以下の申請者に委任している事を、この書面により証明する。 申請者氏名(Applicant Name): <申請者の氏名>(ローマ字表記を含む) 役職(あるいは所属部署): <申請者の役職名あるいは所属部署名> 日付 企業本部所在地 代表電話番号 <代表取締役の氏名>(ローマ字表記を含む) および捺印 ---------- 申請者が代表取締役である場合には、『○○会社代表取締役としての権限によりApple Developer Program へ申請する事を、この書面により証明する』といった文面で作成してください。 2)登記簿謄本(履歴事項全部証明書、又は現在事項全部証明書)、あるいは社印の印鑑証明書(本店のもの)のいずれか1通。 FAX番号: +1-408-974-7683 ※米国の番号となりますので、国際電話のダイヤル方法をご確認ください。 書類をお送りいただく際には、Enrollment ID: ******** と御社の代表電話番号を書類にお書き加えください。日本語表記のものをそのままお送りいただいて、問題ございません。 どうぞ、よろしくお願いいたします。もしご不明な点などございましたら、当方までご連絡ください。Apple Developer Programへのご参加、ありがとうございます。