2010-11-23

iOS アップデート (→ 4.2.1)

一度延期になってしまったが、ようやく iOS のアップデートが来た。早速、iPad、iPhone 3GS、iPod touch (4th)、そして iPhone 3G の順にアップデートを行った。

AirPlay や AirPrint といった新機能もあるけれど、今回のアップデートの肝はなんといっても、iPad と他の iOS デバイスの OS が統合されたことだ。両方を使っていて、微妙な差異にイライラさせられることがなくなる(フォルダとかタスク切り替えとか)。アプリを開発するときにも、現行の OS のバージョンがいくつもあるというのは面倒が多い。

iOS 4.2.1 上の Safari のユーザエージェント

以前にも使った簡単な CGI プログラムで各実機が使うユーザエージェントを調べてみた。

Apple 製デバイスの OS のバージョンとユーザエージェント文字列の対応
Device OS User-Agent
Mac Snow Leopard 10.6.5 (10H574) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4
iPhone 3G iOS 4.2.1 (8C148) Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5
iPhone 3GS iOS 4.2.1 (8C148a) Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5
iPad iOS 4.2.1 (8C148) Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5
iPod touch (4th) iOS 4.2.1 (8C148) Mozilla/5.0 (iPod; U; CPU iPhone OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5

すぐに気付くことは、Mac 用の Safari のバージョンが一番高い(5.0.3)こと。WebKit のバージョンもやはり Mac 用のものが新しい(533.19.4)。意外だったのは、iPhone 3GS だけ OS の番号(ビルド番号かな)が異なっているということだ。3GS のみ 8C148a と末尾に a が付いている。他の iOS デバイスはすべて 8C148 だ。

ともあれ、このアップデートで、ようやく iPad の Safari と他の iOS デバイスの Safari が同じバージョンになった。これまでは、iPad の方が少し古く、CSS3 のサポート状況に若干の違いがあったのだ(→「ウェブページのスタイルを iPad に最適化する方法」参照)。今後のアップデートは足並みを揃えてもらいたいものだ。

関連リンク

関連記事

2010-11-22

Emacs のウィンドウを水平方向に分割する

横 80 桁の呪縛

Emacs との出会いはキャラクタ指向のパソコン上でのことだったこともあり、GUI ベースのものを使うようになっても、その横幅はこれまでずっと 80 桁と決めてきた。「三つ子の……」ということわざを引き合いに出すまでもなく、ごく初期に覚えたことは身に染み付いて消えないのだ。

Emacs のウィンドウを垂直方向に分割することも、ごく初期に覚えたことの 1 つ(split-window-vertically)。キャラクタ端末上の Emacs で複数のファイル(とバッファ)を切り替えて使うためには必須だったし、何より 2 つのファイルの内容を同時に表示できるというのは当時は画期的なことだったのだ。

一方で、ウィンドウの水平方向への分割(split-window-horizontally)についてはほとんど使ったことがなかった。機能としての存在は(ずいぶん前から)知っていたと思うけれど実用性を認めていなかったのだ。実際のところ、キャラクタ端末は言うに及ばず、GUI ベースでも VGA や XGA 程度の画面では 80 桁の画面を複数並べられるほどの横幅はない。

不思議なもので 30 インチのシネマを使うようになっても、その横に 27 インチの iMac を並べるようになっても、ずっと Emacs のウィンドウは 80 桁のもの 1 つだった。ずっとそうでなくてはならない、と思い込んでしまっていたのだ。

呪縛を破る

呪縛(不合理な思い込み)を打破するためには、実際に見ることが一番だ。わたしの場合、それは Emacs の活用術を特集したある雑誌のページだった(→「WEB+DB PRESS Vol.58」)。記事の内容はウィンドウのサイズとも分割とも全然関係ないものだったが、その中に水平方向に 2 分割された Emacs ウィンドウのスクショが掲載されていた。

それを見て目からウロコが落ちる気がした。「あ、そうか。横にも分割できるのか。」「今の画面サイズなら実用になるかも。」「けど、描画が遅いんじゃないかな。」「どうかな、昔とは CPU のパワーも桁違いだから。」「やってみればわかるだろう。」

やってみた。結論から言って、描画がモタつくようなことはない。スクロールして、画面が上からヌルヌルと描かれていくなんてことはない。

ちなみに、水平方向への分割は Ctrl-x 3 に割り当てられている。水平方向の分割が Ctrl-x 2 だから覚えるのにも苦労はない。

水平方向に分割した Emacs ウィンドウ
水平方向に 2 分割
ウィンドウの横幅は 80 桁 x 2。2 つのファイルが同時に表示されている。シネマの画面にはまだまだ余裕がある。
水平方向に 4 分割
ウィンドウの横幅は 80 桁 x 4。最左の画面は縦方向にも 2 分割している。都合、5 つのファイルが同時に表示されている。シネマの画面もほぼ一杯になっている。

複数ウィンドウ vs ウィンドウ分割

単に複数のファイルを同時に表示するだけなら別の方法もある。今の Emacs ならウィンドウを複数開くこともできる。複数ウィンドウに比べて、ウィンドウ分割が優れている点は、単一のキー操作(Ctrl-x o)を繰り返すだけでフォーカスするバッファを切り替えられることにある。慣れてくればほとんど自動的に指が動いてくれる。

Xcode のエディタも複数のウィンドウを開くことができるが、キー操作、それも単一の操作だけでウィンドウを切り替えることはできない。

ま、80 桁 x 4 はちょっと極端すぎるかもな。画面が一杯になって他のウィンドウが表示できなくなる。実用性を考慮すると、80 桁 x 2 ぐらいがちょうど良いのかも。

参考文献

WEB+DB PRESS Vol.58
編
技術評論社 ( 2010-08-24 )
ISBN: 9784774143248

関連記事

2010-11-21

記事一覧を取得する (MacBloggerGlass)

今回は、GData Objective-C Client Library (以下、GData ライブラリ) を使って、Blogger で作ったブログの記事一覧を取得するところまでを作ってみる。

サンプルプログラムがドキュメント

残念ながら公式ドキュメントは充実しているとは言い難い。参考になるのはサンプルプログラム、そしてライブラリ自体のソースコードだ。

Blogger API のサンプルは充実していて、API を使ってやれることは一通り網羅されている。Google アカウントでログインさせた後、ブログの一覧を表示する。一覧からブログを選ぶと、次は選んだブログの記事を取得し一覧で表示する。記事一覧から記事を選べば記事の内容(とコメントがあればコメントも)表示する。記事の内容は更新することも可能になっている。UI こそ単純なテーブルとテキストフィールドを組み合わせたものだが、機能的には Blogger 専用のブログエディタと言っても良いぐらい。

MacBloggerGlass として作りたいと思っているモノの骨格はこのサンプルにある。言い方を換えれば、データの取得やその保持に関してはライブラリに任せてしまえば良いということになる。アプリの作り手としては、ユーザ体験とそれを実現する UI のデザインに専念すれば良い、と。

ま、そのライブラリの使い方を解明するのが、ちょっと大変なんだけどね。Python のライブラリより複雑っていうか、癖があるっていうか。慣れていないだけかな。

MacBloggerGlass のプロトタイプ

右のスクショが今回作ったプロトタイプ(のかけら)のウィンドウだ。使い方は単純、Blog ID を入力して Read ボタンを押すだけ。Blog ID のブログに蓄えられた記事の一覧を取得してウィンドウのテーブルに表示する。使い方同様、機能も単純だ。ちなみに、スクショはこのブログの記事を取得させたときのものだ(Blog ID はダミー)。

以下が今回書いた主要部分になる(MacBloggerGlass/AppController.m より抜粋)。getFeed が Read ボタンが押されたときに実行されるアクションになる。

- (IBAction)getFeed:(id)sender
{
    NSString *blogID = [blogIdField stringValue];
    if ([blogID length] == 0) return;

    [feed removeAllObjects];

    NSString *blogPostURL = @"http://www.blogger.com/feeds/%@/posts/default";
    NSURL* feedURL = [NSURL URLWithString:[NSString stringWithFormat:blogPostURL, blogID]];
    NSLog(@"%@", feedURL);
    
    GDataServiceGoogleBlogger *service = [self bloggerService];
    GDataServiceTicket *ticket;
    
    ticket = [service fetchFeedWithURL:feedURL
                             feedClass:[GDataFeedBlogPost class]
                              delegate:self
                     didFinishSelector:@selector(blogPostsTicket:finishedWithFeed:error:)];

}

- (GDataServiceGoogleBlogger *)bloggerService {
    
    static GDataServiceGoogleBlogger* service = nil;
    
    if (!service) {
        service = [[GDataServiceGoogleBlogger alloc] init];
        
        [service setShouldCacheDatedData:YES];
        [service setServiceShouldFollowNextLinks:YES];
    }
    return service;
}

// blog feed fetch callback
- (void)blogPostsTicket:(GDataServiceTicket *)ticket
       finishedWithFeed:(GDataFeedBase *)afeed
                 error:(NSError *)error
{
    GDataFeedBase *blogFeed = [afeed retain];
    int count = [[blogFeed entries] count];

    for (int i = 0; i < count; i++) {
        GDataEntryBase *entry = [blogFeed entryAtIndex:i];
        Entry *e = [[Entry alloc] init];
        e.title = [[entry title] stringValue];
        e.date = [[entry publishedDate] stringValue];
        [self willChangeValueForKey:@"feed"];
        [feed addObject:e];
        [self didChangeValueForKey:@"feed"];
    }
}

GData ライブラリの使い方の概略は、(1) 利用するサービス(Blogger 等)に対応した GDataServiceGoogle の派生クラスをインスタンス化する、(2) API が返すデータ(Atom フィード)を受け取るクラスを指定して (1) のインスタンスに対して fetchFeedWithURL:feedClass:delegate:didFinishSelector を呼び出す、となる。このとき、didiFinishSelector として、データ取得が完了したときに呼ばれるコールバックメソッドを指定する。

上記のコードでは、65 〜 76 行目の bloggerService がサービスクラスをインスタンス化している部分で、58 行目がデータ取得のための呼び出しになっている。また、コールバックは 79 〜 95 行目の blogPostsTicket:finishedWithFeed:error を指定している。

もとのサンプルでは、Google アカウントとパスワードを入力させてユーザ認証を行っているが、今回のプロトタイプではユーザ認証は省いている。そのために fetchFeedWithURL:... にわたす URL を 51 行目で指定している。アドホックなやり方だと思うが、スマートな(というかライブラリが想定している適切な)方法を調べ切ることができなかったのでこうなった。

表示するブログの選択についてはユーザ体験の観点からどうすべきかを考える必要がある。ブログ ID 自体を指定させる方法が使い易いとは思えないから。ま、おいおい。

関連リンク

関連記事

2010-11-20

GData Objective-C Client Library を組み込む (MacBloggerGlass)

Blogger Glass を Cocoa アプリとして作り直すにあたり、UI を考える前に、まずは GData Objective-C Client Library (以下、GData ライブラリ)を使って Blogger で作ったブログのデータ(Atom フィード)を取得できるようにしておきたい。Blogger Glass を作り始めたときも、最初は(GData ライブラリは使っていなかったが)そこから始めたのだった(→「Blogger で作ったブログの記事一覧を作成する」)。

GData ライブラリを Cocoa アプリ(または Cocoa Touch アプリ)に組み込むには、フレームワークとして(動的に)リンクする方法と、静的ライブラリとしてリンクする方法の二通りがある(→「Adding the Google Data APIs to a Project」)。Cocoa アプリの場合は主に前者を使い、Cocoa Touch アプリなら後者を使う。

では、GData ライブラリをフレームワークとして動的にアプリに組み込むには、Xcode プロジェクトをどう構成すれば良いのだろうか?

1 つの方法は、アプリとは別個にフレームワークをビルドして ~/Library/Frameworks 等に置くことだ。別の方法として、アプリのプロジェクトから GData ライブラリのプロジェクトを参照する方法がある。こちらが、GData ライブラリの配布パッケージにふくまれているサンプルアプリの使っている方法だ。

Cocoa 版 Blogger Glass (MacBloggerGlass と呼ぶことにした)でも、プロジェクトを参照する方法を使うことにした。で、今回の記事では、GData ライブラリのプロジェクトを(サブプロジェクトとして)ふくむ Cocoa アプリプロジェクトの構成手順を説明する。

ただし、以下に述べる手順は試行錯誤の末にたどりついたものなので、この先、アプリの開発を進めていくと何か不都合が出てこないとも限らない。現状、確認できているのは (a) アプリをビルドすれば同時に GData.framework がアプリのビルド構成(Debug または Release)に合わせてビルドされること、(b) アプリのコードから GData.framework の機能を使うことができること、(3) アプリのデバッグ中にステップ実行でフレームワークのソースに入っていけること、の 3 点になる。

手順

概略をまず述べる。GData ライブラリのプロジェクト一式をアプリのプロジェクトにコピーする。その後、Xcode でアプリのプロジェクトに GData プロジェクトを追加し、依存関係の追加やら、ビルドスクリプトの追加やらの調整を行う。お手本にしたのは、GData ライブラリ配布パッケージのサンプル。ビルドスクリプトもそこから拝借してきた。

MacBloggerGlass/
+-- MacBloggerGlass.xcodeproj
|   ...
+-- GData/
    +-- GData.xcodeproj
    |   ...

以下に、ステップごとの手順を示す。

  1. GData Library の配布パッケージを展開する。
  2. Srouce ディレクトリをアプリプロジェクトのディレクトリに GData という名前で移動する。
  3. Xcode でアプリプロジェクトを開く。
  4. メニューから「プロジェクト」>「プロジェクトに追加…」を実行する。
  5. ファイル選択パネルからアプリプロジェクトフォルダ内にある GData フォルダを選び、GData.xcodeproj を選択し、「追加」ボタンを押す。
  6. 「追加したフォルダにフォルダ参照を作成する」を選び、「追加」ボタンを押す。
  7. 「ターゲット」からアプリを選択し、メニューから「ファイル」>「情報を見る」を実行する(⌘ + I でも良いし、ツールバーに「情報」ボタンがあるならそれでも良し)。
  8. 「"ターゲット "(アプリ名)""の情報」パネルが開くので、「一般」タブを開き「直接依存関係」に「GData.xcodeproj」の「GDataFramework」を追加する。
  9. buildCopyScipt を用意する。GData パッケージに付属のものを修正して使う。
  10. メニューから「プロジェクト」>「新規ビルドフェーズ」>「新規スクリプトを実行」を実行する。
  11. 「""(アプリ名)"のスクリプトを実行するフェーズ"の情報」パネルが開くので、「一般」タブで「スクリプト」に「./buildCopyScript」と書き込む。
  12. buildStripHeaders を用意する。GData パッケージに付属のものを修正して使う。
  13. buildStripHeaders スクリプトについても buildCopyScript と同様の手順で、ビルドフェーズとして追加する。
  14. 追加した 2 つのスクリプト実行フェーズの順序を調整する。buildCopyScript は「バンドルリソースをコピー」の前、buildStripHeaders は「バイナリをライブラリにリンク」の後に移動する。
  15. グループとファイルの「GData.xcodeproj」を開き「GData.framework」を、ターゲットの「バイナリをライブラリにリンク」までドラッグしてそこのドロップ。Xcode のウィンドウレイアウトとして「コンデンス」を選んでいる環境なら、この操作は「詳細」ウィンドウを使うとやりやすい(メニューから「表示」>「詳細」を実行すると表示される)。
  16. GData.xcodeproj をダブルクリックするなどして、GData プロジェクトを開き、ビルドオプション等を調整する。とくに、ビルドするアーキテクチャを調整しておかないとリンク時にエラーが出る。双方のアーキテクチャを一致させておけば間違いはない(64-bit intel とかに)。
  17. ビルドを実行。エラーが出ていないことを確認する。
  18. アプリプロジェクトフォルダ内の build/Debug あるいは build/Release に 「アプリ名.app」と「GData.framework」が出来ていれば OK。

正直メンドウだったので、雛形になるプロジェクトを(Cocoa アプリ用、Cocoa Touch アプリ用それぞれに)作っておこうか、と考えている。いっそのこと Xcode のプロジェクトテンプレートにできないかなとも思うが、そのためにはフレームワークを別途ビルドしてシステムにインストールする必要がある(ように思う)。雛形プロジェクトの方が簡単かな。やり方もわかっていることだし。

関連リンク

関連記事

追記@2010-11-30

Google App Engine 用アプリに Python 版 Google Data Client Library を組み込むことについては以下の記事を参照のこと。

2010-11-19

#pragma mark - Xcode のテキストエディタの使い方 #1

Xcode で Mac 用の Cocoa アプリや iOS 用の Cocoa Touch アプリのプロジェクトを作成すると、いくつかの Objective-C のソースファイルが生成される。これらのソースを開くと #pragma mark という記述が書かれていることがある。これは何なのか?

調べて(ググって)みたところ、Xcode のエディタで使われる情報だとわかった。具体的には、Xcode のエディタにある Function Menu の表示を見易くするためのものだった。この Function Menu はエディタの(ウィンドウでもペインでも)上部に置かれた一群の便利機能(Navigation Bar と呼ばれている)の一つで、ソースコード中からクラスや関数、メソッドの宣言、定義を見つけ出し、一覧にしてくれるものだ。メニューの項目を選ぶとエディタで該当する場所に飛ぶことができる。簡易クラスブラウザと言ったところか。

以下は、iPhone アプリのプロジェクトを作った際に生成されるソースファイルから、#pragma mark 部分だけを抜粋したものだ。

@implementation RootViewController


#pragma mark -
#pragma mark View lifecycle

[...snip...]

#pragma mark -
#pragma mark Table view data source

[...snip...]

#pragma mark -
#pragma mark Memory management

[...snip...]

@end

このファイルを Xcode のエディタで開き、Function Menu を表示させたスクショが→になる。元のソースと見比べてみると、#pragma mark がどういう風に使われるのかがおおよそわかる。

  • #pragma mark に「-」を付けると、Function Menu 中の区切り線になる。メニューから選択できない(該当する場所に飛べない)。
  • #pragma mark の後に付けたテキストが Function Menu 中のラベルになる。メニューから選択できる(飛べる)。

「Xcode Workspace Guide: The Text Editor」によれば、この Function Menu で項目として表示できるのはこれだけじゃない。以下のようなコメントも表示させることができる。もちろん、メニューから選んで飛べる。

  • MARK:
  • TODO:
  • FIXME:
  • !!!:
  • ???:

「!!!:」と「???:」はこのマークだけで項目として有効になるが、他の 3 つはマークの後にテキストを付けた場合のみ有効になる。つまり「TODO:」や「FIXME:」だけでは Function Menu に表示されない。「TODO: put code to fetch the URL.」のように書く必要がある。

先に挙げたドキュメントによれば、Function Menu が機能するのは C、Objective-C、C++、Java、Perl、Python、および Ruby に対してのみだとのこと。

近頃の IDE はなんとなく使っていてもそれなりに使えてしまうから、何か困ったことにでも陥らない限りヘルプやドキュメントを読もうと思わない。けれど、それだと便利な機能のことを知らないままになってしまうのだろう。今回の #pragma mark と Function Menu のことも、調べて(ググって)みて初めて知ったわけだし。

ともあれ、Xcode で開発をするなら、エディタに関する「Xcode Workspace Guide: The Text Editor」は、ぜひとも読むべきだ。

関連リンク

関連記事