【iPhone】Push Notificationの実装方法

Posted by: daichi  /  Category: iphone開発

pic1pic2pic3
新作でTwitter,はてな,Google Readerから横断的に情報収集し、マルチポストするアプリを作っているのですが、そこでTwitterのリプライPush機能を実装したので、Push Notificationを実装する方法をまとめてみます。

Push Notificationの流れ

Push Notificationに関する登場人物は、
  • iPhone
  • Apple Push Notification Service(APNs)
  • Provider
の3者です。

iPhoneはいわずもがな、みんなの手元にあるiPhone。APNsはAppleが用意しているPushしてくれるやつです。Providerは、開発者が用意するもので、こいつがPushしたい情報を送る役割を持ちます。

この3者間でのデータの流れは大きくデバイス登録とPush通知の2つのフェーズに分かれます。

デバイス登録

デバイス登録フェーズではAPNsへPush通知するiPhoneを登録します。このときAPNsから一意なデバイストークンが発行され、これをProviderが保持することで、Push通知を送りたいiPhoneをProviderが特定できるようになります。
流れはこんな感じ。
  1. iPhoneにアプリをインストール
  2. アプリ起動時にiPhoneからAPNsへデバイスの認証通知をする
  3. APNsから認証されると、デバイストークンを受け取る
  4. iPhoneからデバイストークンをProviderへ送る

Push通知

Push通知フェーズでは、ProviderからAPNsへPush情報と共に認証情報を送ります。APNsから認証されると、送信先iPhone宛にPush通知が送られ、晴れてiPhone上であのAlertが表示されるようになります。
流れはこんな感じ。
  1. 任意のタイミングでProviderからPush情報と認証情報をAPNsへ送る
  2. APNsから認証されるとiPhoneへPush通知が送られる
  3. iPhone上でポップアップ!
というわけで、以降デバイス登録から順に書いていきます。

鍵を作る

デバイス登録手順1のアプリインストールは飛ばすとして、2のiPhoneからAPNsへデバイス認証通知をするためには、鍵が必要です。

なのでApple Developer Connection – iPhone Dev Center – Overviewへ行って鍵を作りましょう。
手順はこんな感じです。
  1. 左メニューApp IDsからNew App ID
  2. Descriptionには自分のわかりやすい説明を
  3. Bundle Seed IDはGenerate NewでOK
  4. Bundle Identiferは*は使えません。net.longearth.earth等、一意な値となるようにドメイン名を逆から綴ったものにしておく
  5. 以上を入力してsubmit
  6. App IDsのManage画面で登録したApp IDのConfigureリンクを押す
  7. ひとまず開発用の設定をするため、Development Push SSL CertificateのConfigureを押す
  8. Lightbox風に鍵作れといわれるのでキーチェインを起動
  9. キーチェーンアクセス→証明書アシスタント→認証局に証明書を要求…
  10. ユーザのメールアドレスは登録したメールアドレスを
  11. コモンネームは他の証明書の時と同じ名前を
  12. ディスクに保存をチェック→続ける→保存
  13. ブラウザに戻りContinueを押す
  14. さっき作った証明書を選択してGenerate
  15. 認証されたらContinue
  16. Downloadなうして、Done
  17. Lightbox風が終わるので画面上でステータスがenabledになっていることを確認
  18. ダウンロードした証明書をダブルクリックしてキーチェーンに保存
  19. キーチェーンの「自分の証明書」分類にあるApple Development Push Servicesを選択して、メニュー→ファイル→書き出す…→保存(apns-dev-cert.p12)
  20. 同様にキーチェーン画面からApple Development Push Services横の三角を開き、秘密鍵を選択→メニュー→ファイル→書き出す…→保存(apns-dev-key.p12)
長い。。。

ここまでがApple上のドキュメントに書いてあるのですが、p12ファイルとやらをどう使えばよいのかよくわからないので、pemフォーマットに変換(正直この部分はわかってない。男は黙って以下のコマンド群を打ち込む。)
  1. openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
  2. openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
  3. openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem
  4. cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem
これでapns-dev.pemとapns-dev-key-noenc.pemができあがるので、これで鍵の準備は完了。

鍵ができたら、ここで作成したApp IDを指定したProvisioningファイルを作成して、Xcodeにインストール。code signに設定しておくことも忘れずに。

デバイス認証通知

認証通知は簡単です。UIApplicationのregisterForRemoteNotificationTypes:で通知処理を設定します。これだけでcode signされた内容でデバイス認証しに行く様子。アプリ起動直後あたりに仕込んでおきます。ちなみにここでPush時にバッヂ表示させるか、音をならすか、アラート画面を出すかを選択できます。以下はすべてを通知させるような例。

?View Code OBJECTIVE-C
- (void)applicationDidFinishLaunching:(UIApplication *)application {
	[[UIApplication sharedApplication] 
	 registerForRemoteNotificationTypes:
             (UIRemoteNotificationTypeBadge| 
	      UIRemoteNotificationTypeSound|
		UIRemoteNotificationTypeAlert)];
        // 他の処理...
}


デバイストークンを受け取る

APNsから認証されるとデバイストークンを受け取ることができます。認証後はUIApplicationのapplication:didRegisterForRemoteNotificationsWithDeviceToken:メソッドが呼ばれるのでここで受け取ります。

?View Code OBJECTIVE-C
- (void)application:(UIApplication*)app 
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)devToken{ 
	NSLog(@"deviceToken: %@", devToken); 
	self.devToken;
        [self sendProviderDeviceToken:devToken];
}

sendProviderDeviceToken:メソッド内でProviderへデバイストークンを送る処理をしています。NSLogで送られてきたdevTokenをログ出力してますが、このとき表示される32バイトの値がデバイストークンです。後のproviderでのテストのために控えておきます。
認証エラー時は以下のメソッドが呼ばれます。

?View Code OBJECTIVE-C
- (void)application:(UIApplication*)app 
didFailToRegisterForRemoteNotificationsWithError:(NSError*)err{ 
	NSLog(@"Errorinregistration.Error:%@",err); 
}

code signが間違っている等、分かりやすい日本語で表示してくれているので助かります。

Providerへデバイストークンを送る

ここは好きなようにProvider宛にデバイストークンをPostしてあげてください。
ちなみにこんな感じでポストできるはずです。

?View Code OBJECTIVE-C
- (void)sendProviderDeviceToken:(NSData *)token {
		NSMutableData *data = [NSMutableData data];
		[data appendData:[@"device=" dataUsingEncoding:NSUTF8StringEncoding]];
		[data appendData:token];
 
 
		self.request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"ホスト名"]];
		[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];		
		[request setHTTPMethod:@"POST"];
		[request setHTTPBody:data];
		[NSURLConnection connectionWithRequest:request delegate:self];
}


サーバ側では、このデバイストークンを控えておきます。実用的な所でいうと、このデバイストークンと何かしらのユーザを識別するIDをサーバへ送ることになると思います。
ここまでで、デバイスの登録が完了です。

ProviderからAPNsへPush通知を送る

ProviderからAPNsへの通知は以下のようなフォーマットで送る必要があります。

aps_provider_binary
始めの3バイトは固定で、0,0,32。16進では0×00,0×00,0×20です。その後デバイストークンと続き、0×00,送信データサイズ、送信データという流れです。
このあたりはバイナリにまとめたものをファイルに書き出し、ターミナルから
xxd ファイル名
として中身を確認することをおすすめします。アプリ側のNSLogで出力したデバイストークンは16進表記なので、その値がきれいに上のデバイストークンの場所に収まっていればだいじょぶだと思います。

送信処理をrubyで書くとこんな感じです。以下のスクリプトを実行する時は、作成した証明書と秘密鍵を同じディレクトリに置いておいてください。

#!/usr/bin/ruby
 
require 'openssl'
require 'socket'
 
    device = ['デバイストークンのバイナリ値']
 
    socket = TCPSocket.new('gateway.sandbox.push.apple.com',2195)
 
    context = OpenSSL::SSL::SSLContext.new('SSLv3')
    context.cert = OpenSSL::X509::Certificate.new(File.read('apns-dev.pem'))
    context.key  = OpenSSL::PKey::RSA.new(File.read('apns-dev-key-noenc.pem'))
 
    ssl = OpenSSL::SSL::SSLSocket.new(socket, context)
    ssl.connect
 
    payload = <<-EOS
{
  "aps":{
    "alert":"New Message!",
    "badge":1,
    "sound":"default"
  }
}
EOS
    (message = []) << ['0'].pack('H') << [32].pack('n') << device.pack('H*') << [payload.size].pack('n') << payload
 
 
    ssl.write(message.join(''))
    ssl.close
    socket.close

上の例では、Alert表示に”New Message!”を出して、アプリアイコンバッヂに1を表示させ、デフォルトの音をならすような通知設定をしています。設定方法の詳細はApple Push Notificationサービス プログラミングガイドにあります。

証明書と鍵さえ正しいものを使っていれば、ローカルのmacからでもPushすることができるので、ローカルでごにょごにょ確認するのがいいと思います。ここまでくれば、送る相手のデバイストークンさえ分かればPushできるので、登録されたデバイストークンを全取得する方法を用意しておけば、ローカルから最新情報告知なんていう使い方もできそうです。

特に気の知れた相手のデバイストークンが分かっていれば、「うんこ」なんて通知もできてしまいそうです。もちろん自重します。絶対。

これから

あとはアプリの任意のタイミングで上記のように送りたい相手のデバイストークンを取得して、メッセージを送信することでPushすることができます。
麻雀アプリなら上家がパイを切った後に、Providerへ通知させて、次の相手のデバイストークンを引っ張ってきてPushという形になるでしょう。

Twitterのリプライ通知をどのようにしようかすごく悩んでいたのですが、Stream APIが使えそうです。
Twitter API Wiki / Streaming API Documentation
試してみた所、まだアルファテスト版ということでだいぶ不安定な気もします。Twit Pushとかどうやっているのか気になります。

長いことおつかれさまでした。
しかし、めんどくさい。
もうちょっと楽にできないかな。

参考

後で気づいたけど日本語のドキュメントがあった。
iPhone Dev Center
cocoa*life – Apple Push Notification Serviceを利用した、iPhone クライアントと、Rubyによるサーバの作成。
How to build an Apple Push Notification provider server (tutorial) « Boxed Ice Blog

【iPhone】Objective-CでAtomPubを簡単に操作できるライブラリ公開してみた

Posted by: daichi  /  Category: 開発補助

daichi1128’s DCAtomPub at master – GitHub

はてなブックマークの登録をiPhoneアプリから行うにははてなのAtomPub APIを叩く必要がありますが、WSSE認証やらsha1やらbase64エンコードやらISO8061やらXMLポストやらでハマリ倒してしまったので、二度とつまずかないためにもライブラリ化してみた。
ライブラリというほど大げさなものでもないのですが、ソースコード公開デビューということで大目にみてやってください。

Objective-CでAtomPubを簡単に操作するライブラリDCAtomPubをgithubに公開しました。
ライセンスは修正BSDライセンスです。

AtomPubに関する情報はこちらから
日本語訳はこれ。ソフトウェア分野の研究開発 / RFC 5023 Atom Publishing Protocol 日本語訳 | Ricoh Japan

はてなブックマークAPIはこちらから。
はてなブックマークAtomAPIとは – はてなキーワード

セットアップ

ターミナルから以下のコマンドでダウンロード(要gitクライアント)

git clone git://github.com/daichi1128/DCAtomPub.git

githubからダウンロードしたら、Xcodeで起動し、DCAtomPubグループ内のファイル群を自分のプロジェクトにコピー。はてな用クライアントも必要ならHatenaExample内のファイルもコピー。

使い方

WSSE認証

DCAtomPubClinetの初期化メソッドの引数にユーザ名、パスワードを渡してあげれば面倒なWSSE認証の準備は勝手にDCAtomPubClientがやります。

?View Code OBJECTIVE-C
NSString *username = @"user";
NSString *password = @"pass";
DCAtomPubClient *atomClient = [[DCAtomPubClient alloc] initWithUsername:username password:password];

正確にはリクエストを投げるまでにユーザ名、パスワードがセットされていればリクエスト送信時にうまいことやってくれます。こんな形でもよいです。

?View Code OBJECTIVE-C
DCAtomPubClient *atomClient = [[DCAtomPubClient alloc] init];
atomClient.username = @"user";
atomClient.password = @"pass";

POST

AtomPubのPOSTメソッドはDCAtomPubClientクラスのpost:XMLString:メソッドで行います。
はてぶでは登録時にPOSTメソッドでXMLを投げます。
サンプルコードはこんな感じです。

?View Code OBJECTIVE-C
        NSString *username = @"user";
        NSString *password = @"pass";
	/** 
	 *AtomPubクライアントではてなブックマークを追加
	 */
	DCAtomPubClient *atomClient = [[DCAtomPubClient alloc] initWithUsername:username password:password];
	atomClient.delegate = [[DummyDelegate alloc] init];
 
	NSString *hatenaPostURL = @"http://b.hatena.ne.jp/atom/post";
	NSString *postXMLTemplate = 
	@"<entry xmlns=\"http://purl.org/atom/ns#\">"
	@"<link rel=\"related\" type=\"text/html\" href=\"%@\" />"
	@"<summary type=\"text/plain\">%@</summary>"
	@"</entry>";
 
	// POSTメソッドでXMLを送信
	[atomClient post:hatenaPostURL XMLString:[NSString stringWithFormat:
		  postXMLTemplate, 
		  @"http://iphone.longearth.net/itasktimer/",@"DCAtomPubから" ]];

PUT

はてなブックマークの編集時にAtomPubのPUTメソッドを使います。使い方はPOST時と同じように、DCAtomPubClientのput:XMLString:を呼べばOKです。

DELETE

はてなブックマークの削除時にAtomPubのDELETEメソッドを使います。使い方はPOST、PUTと同じです。DCAtomPubClientのdelete:XMLStringを呼びます。はてなブックマークの場合はXMLStringはnilでOKです。

はてなクライアントの使い方

AtomPubの扱いは上のDCAtomPubClientでだいぶ簡略化できたけど、いちいちXMLを調べて組み立てるのが面倒だ。なのではてな用AtomPubクライアントも作りました。
DCHatenaClient。DCAtomPubClientを継承してます。

ブックマークを追加する

DCHatenaClientを使ってブックマークを登録するには、DCHatenaClientの
- (void)post:(NSString *)bookmarkURL comment:(NSString *)comment;
メソッドで行います。
引数にはブックマークするURLとコメントを渡してあげます。
サンプルはこんな感じです。

?View Code OBJECTIVE-C
	/** 
	 * はてなブックマーク用クライアントではてなブックマークを追加
	 */
	DCHatenaClient *hatenaClient = [[DCHatenaClient alloc] initWithUsername:username password:password];
	[hatenaClient post:@"http://iphone.longearth.net" comment:@"DCHatenaClientから"];

だいぶすっきりします。

ブックマークを編集する

ブックマーク編集は
- (void)edit:(NSString *)eid title:(NSString *)title comment:(NSString *)comment;
で行います。
引数に編集するエントリID、変更後のタイトル、変更後のコメントを渡します。はてぶAPI的にタイトルとコメントいずれか必須らしいのでそうしてください。変更したくないものはnilを渡します。

?View Code OBJECTIVE-C
	DCHatenaClient *hatenaClient = [[DCHatenaClient alloc] initWithUsername:username password:password];
	[hatenaClient edit:@"15151515" title:@"変更後のタイトル" comment:nil];

すっきりですね。

ブックマークを削除する

ブックマーク削除は
- (void)delete:(NSString *)eid;
で行います。

?View Code OBJECTIVE-C
	DCHatenaClient *hatenaClient = [[DCHatenaClient alloc] initWithUsername:username password:password];
	[hatenaClient delete:@"15151515"];

もうAtomPub恐るるに足らずです。

その他

デリゲートとしてDCAtomPubDelegateを用意しています。呼べるものはNSURLConnection系を周到してます。こんな感じです。

?View Code OBJECTIVE-C
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection data:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;


リクエストを投げる前に
dcAtomPubClinet.delegate = self;
などとしてセットしてあげればNSURLConnectionと同じ感覚で使えるはずです。

mixiの隠しAPIもAtomPubらしいので、うまくいけば使えるかもしれません。まだ試してない。
※本来ならAtomPubのルートエンドポイントからアクセス可能なURLを引っ張ってくるのがAtomPub的に正しいことらしいのですが、面倒なので今回はそこまでしてません。

参考

はてなブックマークAtomAPIとは – はてなキーワード
CocoaでWSSE認証 – 24/7 twenty-four seven
cocoa_crypto_hashing: Summary
NSDataにBase64のエンコード・デコード機能を追加する

【Xcode】設定しておくと便利なカスタマイズいろいろ

Posted by: daichi  /  Category: 開発補助

いろいろカスタマイズして便利にします。

外観

デバッグコンソールなどを1つのウィンドウに統合する

シミュレータでアプリを起動すると、デバッガコンソールがXcodeの後ろに隠れてしまい、いちいちフォーカスを切り替えるのが面倒。そんなときはこの設定を。
Xcode→環境設定→全般→レイアウト→オールインワン

アプリ実行時にデバッガを自動的に表示する

Xcode→環境設定→デバッグ→開始時→コンソールとデバッガを表示

__MY_COMPANYNAME__を変更する

ファイルを新規に作成する時にファイル作成者の情報等が自動生成されるが__MY_COMPANYNAME__が気持ち悪い。そんな時は、コンソールから以下のコマンドを叩く。YourNameHereを表示したい名前に置き換えて。

defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions
   '{ORGANIZATIONNAME="YourNameHere";}'



プリプロセッサ

デバッグ用マクロ

デバッグ時はNSLogメソッドをよく使うが、リリースビルドにはNSLogを使いたくない。そんな時はプリプロセッサでDebugモードの時だけ、NSLogを吐くマクロを定義すればいい。
Global.h

?View Code OBJECTIVE-C
#ifdef DEBUG 
#define LOG(...) NSLog(__VA_ARGS__)
#else
#define LOG(...) 
#endif



info.plistでDebug構成時のみ、GCC_PREPROCESSOR_DEFINITIONS項目にDEBUG文字列を設定する。

すると、LOG(@”hogehoge”)でDebug構成時のみ、NSLogを吐けるようになる。

テンプレート

IBを使わないテンプレートを作成する

Interface Builderは最近は使わなくなったので、新しいプロジェクトを作る場合に、いちいち削除したりするのが面倒。そんな時はプロジェクトテンプレートをカスタマイズする。

Xcodeのプロジェクトテンプレートは、以下の場所においてある。
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application

ユーザ定義のテンプレートは以下の場所に置くとXcodeに認識される。
~/Library/Application Support/Developer/Shared/Xcode/Project Templates/Application/

Windowベースのアプリケーションをベースにカスタマイズする。オリジナルのWindow-based Applicationをユーザ定義の方にコピーしてWindow-based-non-IB Applicationにリネーム。

ディレクトリ内の___PROJECTNAME___はすべてプロジェクト名に置き換えられる。

変更するのは以下。
  • info.plistのMain nib file base name→空に
  • main.m内のUIApplicationMainメソッドの第4匹数に@”___PROJECTNAME___AppDelegateを”
  • MainWindow.xib→削除
  • ___PROJECTNAME___.xcodeproj/project.pbxproj→MainWindow.xibに関する部分をすべて削除
これでxibファイル抜きのテンプレートができあがり。

個人的に___PROJECTNAME___AppDelegateという名前が長ったらしくてあまり好きではないので、これをAppDelegateにした。
この時の変更箇所は以下。
  • main.m→@”___PROJECT_NAME___AppDelegate”を@”AppDelegate”へ
  • ___PROJECT_NAME___AppDelegate.h→ファイル名をAppDelegate.hへ。中身も該当箇所を修正
  • ___PROJECT_NAME___AppDelegate.m→ファイル名をAppDelegate.mへ。中身も該当箇所を修正
  • ___PROJECT_NAME___.xcodeproj/project.pbxproj→___PROJECT_NAME___AppDelegate部分をすべてAppDelegateへ修正

.gitignore、.gitattributeをテンプレートに含める

gitを使いだすとgitの設定ファイルである.gitignoreでXcode用ファイル達を除外したくなるが、これを毎回プロジェクトを作る度にコピーしてくるのは面倒なので、テンプレートに含めてしまう。やり方は上のテンプレートディレクトリに.gitignore、.gitattributeファイルを入れるだけ。
.gitignoreは

# xcode noise
build/*
*.pbxuser
*.mode1v3
 
# old skool
.svn
 
# osx noise
.DS_Store
profile


.gitattributesは

*.pbxproj -crlf -diff -merge


ここまでのテンプレート設定ディレクトリをいちおのせておく。これをダウンロードして、ローカルの
~/Library/Application Support/Developer/Shared/Xcode/Project Templates/Application/
に置けば、使えると思う。
Window-based-non-IB Application
これには次のログマクロテンプレートも含まれている。

ログマクロをテンプレートに含める

さきほど作ったログマクロをテンプレートに含めるのは、テンプレートディレクトリ内にログマクロを記述したファイルを追加すればOKだが、ファイルを開発時にファイルを新規作成するたびに、
#import “Global.h”
とするのは面倒だ。

なので、ファイル作成時に既に#import “Global.h”を追記されているファイルテンプレートを作成する。

オリジナルのファイルテンプレートは以下に置いてある。
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/File Templates/Cocoa Touch

このCocoa Touchディレクトリをユーザ定義用ファイルテンプレートディレクトリである
~/Library/Application Support/Developer/Shared/Xcode/File Templates/
へコピー。

あとはファイルを好きなように変更すればそれがテンプレートになる。

Cocoa Touchのすべてのファイル作成時にGlobal.hを含むように設定したファイルテンプレートはこれ。
Cocoa Touch Class
これを
~/Library/Application Support/Developer/Shared/Xcode/File Templates/
へ入れれば、ユーザ定義ファイルとして使える。


よく使うフレームワークをテンプレートに含める

例えば、Three20だったり、JSONフレームワークだったり、GTMだったり、こういうよく使うフレームワークはあらかじめ使える準備の整ったプロジェクトテンプレートが欲しくなる。これも上のやり方同様、コピーしたローカルのテンプレート周りをいじくり倒して設定できる。

マクロ

よく使うメソッドをマクロとして登録する

Xcodeでは、Ctrl+.でマクロを呼び出せる。
例えばinitと打って、Ctrl+.を押すと、NSObjectのinitメソッドが挿入される。他にもlog、deallocなどが予めマクロとして登録されている。
が、ViewController周りのメソッド達は登録されていないので、いちいちviewWillAppear〜などとよく使うメソッドを入力しなければならない。これは面倒だ。ということでマクロを登録する。

オリジナルのマクロ定義ファイルは以下にある。
/Developer/Applications/Xcode.app/Contents/PlugIns/TextMacros.xctxtmacro/Contents/Resources/ObjectiveC.xctxtmacro

これをユーザ定義のマクロファイル置き場である以下にコピーする。
~/Library/Application Support/Developer/Shared/Xcode/Specifications

マクロとして
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
を追加する場合は、ファイル内に以下のような記述を追加する。

        {
            Identifier = objc.didselect;
            BasedOn = objc;
            IsMenuItem = NO;
            Name = "didSelectRowAtIndexPath";
            TextString = "- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {¥n¥t&lt;#statements#&gt;¥n}";
            CompletionPrefix = didselect;
        },


内容の説明
  • Identifier→ユニークな識別ID。
  • Name→Xcodeで編集→テキストマクロを挿入→Objective Cと行った時に表示される名称
  • TextString→マクロ実行時に挿入される文字列
  • CompletionPrefix→マクロ発動文字列。例の場合、didselectと入力してCtrl+.を押すとマクロが実行される
他の項目はおおむね上の例のままで問題ない様子。

ひとまず
viewWillAppear, viewDidAppear, viewDidLoad, viewWillDisappear, viewDidDisappear, cellForRowAtIndexPath, didSelectRowAtIndexPathなどを登録してみた。なかなか悪くない。

vwaと入力してCtrl+.でviewWillAppear発動。他もvda、vdl、vwd、vdd、cellfor、didselectで発動するのでいい感じ。

この設定済みマクロはこちらから。
ObjectiveC.xctxtmacro

参考

【Objective-C】カテゴリ使って既存クラスに機能を追加する

Posted by: daichi  /  Category: iphone開発

Objective-Cのカテゴリという仕組みを使うと、1つのクラスを複数のクラスに分割したり、クラスに新しい機能を追加したりできる。

このカテゴリの強力なところは、NSStringのようなCocoa Frameworkで提供されているクラス達に対しても、メソッドを追加できるところ。

例えば、ベーシック認証をリクエストのヘッダ内に認証情報を持たせて行いたい。その時、ユーザ名、パスワードはbase64エンコードが必要になるのだけど、既存のNSStringにはそんな便利メソッドはない。

そんな時は、このカテゴリを使ってNSStringにbase64エンコードを行うメソッドを追加してしまう。

このカテゴリによるクラス拡張はオープンソースを眺めてると必ず出てくる。
json-framework – Google Code
google-toolbox-for-mac – Google Code

サンプルコードは以下。

NSString+Base64Encode.h

?View Code OBJECTIVE-C
#import 
 
@interface NSString (DCEncode)
+ (NSString*)stringEncodedWithBase64:(NSString*)str;
@end


NSString+Base64Encode.m

?View Code OBJECTIBE-C
#import 
#import "NSString+Base64Encode.h"
 
@implementation NSString (DCEncode)
 
+ (NSString*)stringEncodedWithBase64:(NSString*)str
{
	static const char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
	const char *s = [str UTF8String];
	int length = [str length];
	char *tmp = malloc(length * 4 / 3 + 4);
 
	int i = 0;
	int n = 0;
	char *p = tmp;
 
	while (i &lt; length) {
		n = s[i++];
		n *= 256;
		if (i &lt; length) n += s[i];
		i++;
		n *= 256;
		if (i &lt; length) n += s[i]; 		i++; 		 		p[0] = tbl[((n &amp; 0x00fc0000) &gt;&gt; 18)];
		p[1] = tbl[((n &amp; 0x0003f000) &gt;&gt; 12)];
		p[2] = tbl[((n &amp; 0x00000fc0) &gt;&gt;  6)];
		p[3] = tbl[((n &amp; 0x0000003f) &gt;&gt;  0)];
 
		if (i &gt; length) p[3] = '=';
		if (i &gt; length + 1) p[2] = '=';
 
		p += 4;
	}
 
	*p = '\0';
 
	NSString *ret = [NSString stringWithCString:tmp];
	free(tmp);
 
	return ret;
}
 
@end


ヘッダファイルでは@interfaceの後にある(DCEncode)というのがカテゴリの名前。
実装ファイルでは@implementationの後にある(DCEncode)と対応してる。

Objective-Cではファイル名とクラス名は特に関連がなくてもOKなのもポイント。

自作クラスでもこのようにカテゴリを分けて記述することで、複数ファイルに分割してプログラムができる。

ただし、変数は親ファイルで定義しないといけないので注意。

これは動的なメソッド追加が可能なことを意味しているのだけど、詳細は以下のサイトに譲る。
【コラム】ダイナミックObjective-C (8) カテゴリ – 動的なメソッドの追加によるクラスの拡張 | エンタープライズ | マイコミジャーナル

base64エンコードのロジックは夏ライオンのソースを参考にさせていただきました。
感謝。

参考
takuma104’s ntlniph at master – GitHub

【iPhone】海外レビューサイト38+2のUUやPVをGoogle Ad Planerで調べてみた

Posted by: daichi  /  Category: 販売促進

総額1万ポイントで。1/12日にiPhoneアプリを出すのですが、海外でのプレスリリース先を探しています。 海外のiPhoneアプリ系につよい有力メディア、ブログを教えてください.. – 人力検索はてな
iPhoneアプリをレビューしてくれそうな海外サイト30個と連絡先リスト

先人の遺産から海外レビューサイトのUU/PVをGoogle Ad Plannerで調べてみた。
Google Ad Planner(アドプランナー)で“あのサイトのPVやUU”を調べてみよう! | Web担当者Forum

Ad Plannerの作り上ドメイン単位でしかリサーチできないので、lifefackerのようなレビューページがサイトの1つのコンテンツとして用意されてる場合、数字にずれが出てくると思うけど、そこはあくまで目安程度に。

海外レビューサイト

以下PV順。

続きを読む

【mac】開発中のアプリを先行公開する時に使えそうなフリーの動画キャプチャソフト

Posted by: daichi  /  Category: 開発補助

開発中のアプリを先行公開する時に使えそうなmac用ディスプレイ動画キャプチャフリーソフト。

Leopard専用に作成された動画キャプチャ・ソフトウェア『スクリン・クロラ』 | Macの手書き説明書
youtube用、iPhone用サイズなどのキャプチャサイズが用意されていて、サクッととれるのでよさげ。

iShowU | Macの手書き説明書
フリーでも使えるけど、画面にロゴが入ってしまうみたい。

ポッドキャストとけものみち「BONCHICAST Log」 – デスクトップを動画でキャプチャ「Jing」[Win&Mac]
だいぶ重くてカクカクしてしまう。

スクリンクロラがよさそう。

関連のあるアプリ

iTask Timer
iTask Timer 2009/06/05 リリース
仕事効率化 230円
時間管理アプリ
app_store_badge

【mac】右クリックからパスをコピーを追加するプラグイン

Posted by: daichi  /  Category: 開発補助

右クリックでパスをコピー

AmCopyPathCMX

macに右クリックからパスをコピーを追加するプラグイン。

なぜmacにはデフォルトでファインダーのパスをコピーする方法がないのか不思議。
mac使いはどうやってるのか知りたい。

総合売上

Posted by: daichi  /  Category: ダウンロード

有料アプリを販売してから2ヶ月くらいたちました。

iTunes Connectのダウンロードデータは日別のものだと過去一週間分になってしまうけど、週別のものは登録以降すべてのものが残っているので、ここに晒します。

iPhoneのAppSalesの画面が見やすいのでこのキャプチャで。
appsales-mobile – Google Code

AppSalesについては他のサイトでもたくさん紹介されているのでここでは触れません。
AppleWalker 開発者には必見のツールアプリAppSales
(●´艸`)ムフフ AppSales:各国別に自分のiPhoneアプリの売り上げを見ることができるiPhoneアプリのソースコードが公開されています。


トータルで今50000円くらい。

売上

開発用iPod touch代とかdeveloper Program代とかiPhone代とかiMac代とか開発コストとか考えると真っ赤っ赤w

新作で挽回したいっ。

関連のあるアプリ

islot_icon
iSlot Pro 2009/03/13 リリース
ファイナンス 350円
パチスロ収支管理アプリ
app_store_badge

【ニコニコ】iPhoneアプリ販促用デモ動画を作成してみた

Posted by: daichi  /  Category: 開発後



旅行やら病気やらでだいぶ時間が空いてしまった。
が、懲りずにiPhone開発は続けます。

ところで、アプリダウンロード数が案の定伸びない。
無料アプリのiPachiがだいたい3/27時点で合計1500ダウンロードくらい。
で、有料アプリのiSlot Proがまだ50弱。

まだiPachiと比べるとiSlot Proが販売されてから日が浅いことや、価格設定などにも原因はあると思いますが、何よりも大きいのが、そもそもアプリの存在を知らないというところだと思います。

続きを読む

iPachiがファイナンスで3位!

Posted by: daichi  /  Category: 開発後

3位

マジですか?
いきなりファイナンスの無料アプリで3位になってます!

プロモーションコードも配ってないし、このブログ以外どこにも告知していないし、App Store上でもファイナンスのリリース日で表示した時にめいいっぱいスクロールした下の方の目立たない場所に控えめに表示されてたので、3位になるなんてありえるのか少し府に落ちないところがあります。

続きを読む

Get Adobe Flash playerPlugin by wpburn.com wordpress themes