FMDBExtensionsを公開しました

Kazzz/FMDBExtensions · GitHub
FMDBExtensionsはiOSのSQLite用ライブラリィであるFMDBとandroidのSQLiteDatabaseクラスにインスパイアされて書いた、iOSのSQLiteデータベース用ライブラリィです。

経緯

iOSのアプリケーションを開発する際にSQLiteデーターペースを扱うための高次のライブラリィが提供されていない所に、FMDBというオープンソースライブラリィがあることを知り、大変に便利に使わせて頂きました。

ccgus/fmdb · GitHub

一方でandroidSQLiteにアクセスする際にはSDK標準で提供されているSQLiteDatabaseクラスを使用してました。SQLをあまり意識せずに使える一連のAPI群は非常に便利であり、生のSQLを書くのが嫌いな私はandroidと同様の使い勝手をFMDBに求めました。それがFMDBExtensionsのきっかけです。

前提条件

FMDBを使える環境とFMDB本体(Xcode、ARC下での開発を前提にしています。)
私はARC,Blocksが有効になった以降にiOSのプログラミングを開始しているためにARCを前提にしています。

提供するもの

FMDBExtensionsはFMDatabaseクラスとFMResultSetのカテゴリとして拡張機能を提供します。

  • FMDBExtensions

FMDBExtensionsを使用する場合はこのヘッダをimportします

  • FMDatabase+FMDBExtensions

FMDatabaseの機能を拡張します

- (BOOL)isOpen;
- (BOOL)isClose;
- (int)rowCount:(NSString*)tableName columnName:(NSString*)columnName whereClause:(NSString*)whereClause whereArgs:(NSArray*)whereArgs;
- (BOOL)deleteAllRows:(NSString*)tableName;
- (long)insert:(NSString*)tableName nullColumnHack:(NSString*)nullColumnHack values:(NSMutableDictionary*)values;
- (int)delete:(NSString*)tableName whereClause:(NSString*)whereClause whereArgs:(NSArray*)whereArgs;
- (int)update:(NSString*)tableName values:(NSMutableDictionary*)values whereClause:(NSString*)whereClause whereArgs:(NSArray*)whereArgs;
- (FMResultSet*)query:(NSString*)tableName columns:(NSArray*)columns selection:(NSString*)selection selectionArgs:(NSArray*)selectionArgs groupBy:(NSString*)groupBy having:(NSString*)having orderBy:(NSString*)orderBy;
- (FMResultSet*)query:(NSString*)tableName distinct:(BOOL)distinct columns:(NSArray*)columns selection:(NSString*)selection selectionArgs:(NSArray*)selectionArgs groupBy:(NSString*)groupBy having:(NSString*)having orderBy:(NSString*)orderBy limit:(NSString*)limit offset:(NSString*)offset;
- (void)logDBError:(NSString*)sql;
- (void)open:(void (^)(void))contextBlock;
- (void)open:(void (^)(void))contextBlock flags:(int)flags;
- (void)openWithTransaction:(void (^)(void))contextBlock;
- (void)openWithTransaction:(void (^)(void))contextBlock flags:(int)flags;
- (int)getVersion;
- (void)setVersion:(int)version;

ヘッダに書かれているコメントとテスト(FMDBExtensionsTests)をみれば見れば、FMDBを知っているプログラマの方であれば何をしているかは大体分かると思います。

  • FMResultSet+FMDBExtensions

FMResultSetを拡張します

- (id)objectForColumnNameNotNull:(NSString*)columnName nullHack:(id)nullHack;
- (NSString*)stringForColumnNotNull:(NSString*)columnName nullHack:(id)nullHack;

同上です。

  • BBSQLiteOpenHelper

androidのSQLiteOpenHelperと同様のヘルパクラスを提供します。イニシャライザで指定された名前のデータベースがパッケージバンドルにあれば、それをDocumentディレクトリにコピーするという前処理を行います。邪魔な場合は消すかオーバライドして使います。

androidに存在しているデータベースのロック機能は未だ実装していません。必要であれば書くかもしれません。

使い方の例

Blocksを使ってデータベースのコンテキストブロックを構成できるようになっていますので、以下のようにcloseを省略したイディオムを利用できます。(もちろん各自で明示的にオープン、クローズを行うこともできます)

[db open:
 ^{
     NSString* sql = [[NSString alloc] initWithFormat:@"SELECT * from %@ WHERE %@ = ? and %@ = ?", TBL_NAME, COL_USER_ID, COL_YEAR_MONTH];
     FMResultSet* rs = [db executeQuery:sql, @"Kazzz", @"201303"];

     BOOL rsResult = (rs.next);
     STAssertTrue(rsResult, nil);

     STAssertEqualObjects([rs stringForColumn:COL_USER_ID], @"Kazzz", nil);
     STAssertEqualObjects([rs stringForColumn:COL_YEAR_MONTH], @"201303", nil);
     STAssertEqualObjects([rs stringForColumn:COL_DAY], @"02", nil);
 }];

Blocksの制限や癖(循環参照)の問題はそのまま残っていますので、理解されている方が使用してください。

同様にコンテキストブロックの先頭でトランザクションを開始して、終了時にコミットを実行するコンテキストブロックも書けます。

    [db openWithTransaction:
     ^{
         [db delete:TBL_NAME
           whereClause:[[NSString alloc] initWithFormat: @"%@ = ? and %@ = ? and %@ = ?"
                        , COL_USER_ID, COL_YEAR_MONTH, COL_DAY]
             whereArgs:@[@"Kazzz", @"201303", @"02"]];

     }];

例外が発生した場合は自動的にロールバックするようになっていますので、気に入らない場合は自分でブロックを定義してみてください。カテゴリによる拡張は自由ですから。

テスト

FMDBExtensionsTests.mにより、データベースのCRUDに関しての簡単なテストをしています。全然足りないと思いますが、必要な方は自分でテストを書いてください。

ライセンス

FMDBと同様にMITライセンスで配布します

謝辞

FMDBを書かれたAugust Mueller氏とgithub、及びコミュニティの皆さんに感謝します。

AirPrintはBonjourを話すとは限らない

!=
先日AirPrint対応のプリンタをandroidから制御してみようというエントリを起こしたのだが、これは困難なのかもしれないと思いはじめている。

無知な私はAirPrintはBonjourに対応した印字機能を持つプリンタのことを指すのではないかと勝手に思い込んでいたのだが、そうではなく、AirPrintはあくまでApple独自の特定の機器のドライバを必要といないゼロ・コンフィグレーション技術を用いたプロトコルであり、Bonjourの基礎的な技術は使用していると思われるが、AirPrint対応のプリンタがBonjourを使って全て制御できるわけではないということぽい。

ネットの情報を総合するとAirPrintで解っていることは

    • AirPrintは印字制御にインタネット印刷プロトコル(IPP)を使用する
    • AirPrintはプリンタの探索にマルチキャストDNS (mDNS)を使用する
    • AirPrintサービスタイプとして"_ipp"のサブタイプである"_universal"を使用する
    • AirPrintプリンタの情報を取得するために追加のテキストレコード"URF"を必要とする

この辺位。

用途としては同じ「ゼロ・コンフィグレーション」であってもAirPrintはBonjourとは違い、コミュニティに対して詳細な仕様は公開されておらず※、androidからAirPrintプロトコルを操作するソフトウェアは公開されていない。

上記の情報を見る限りでは、Boujourを使ってプリンタをネットワークにサービスとして登録し、プリンタを使う側は同じくBonjourを使って探索、発見して、IPPプロトコルを使ってプリンタとお話すれば良いように思えるが、非公開である以上、推測でしかない。

※ 他の仕様と同様にAppleNDAを締結したベンダーにのみ公開されていると思われる。

AirPrintとandroid

AirPrint対応のプリンタを買う予定なのだが、MacやiOSから普通に使えるのは当たり前として、androidからも同様にアクセスしたい。そのためのアプリがあるであろうことは想像がつくのだが、折角なので自力でやってみたいのだ。

1. 環境の準備

開発環境は整っているのであとはデバイスつまりAirPrintに対応したプリンタをとっとと用意すればいいことなのだが、財政事情もあり実際に買うのは夏以降になりそうなので、それまではプリンタを買わずに環境を用意したい。

聞いたところではWindows上でiTunes共有を有効にした場合、Bonjour※サービスとして登録されるそうなのでそれを使っても良いのだが、できれば想定しているプリンタを使いたいところだ。

2. Printer Simulator


開発環境としてXcodeをインストールしていれば、iPhoneシミュレータがインストールされているはずだが、実はそれと一緒にプリンタシミュレータがインストールされている。


これを起動するとこのように5つの仮想プリンタデバイスがシステムに登録される


それぞれの仮想プリンタは全てBonjourサービスとして登録されているので、これをテストに使うことにする。


このようにiOSシミュレータからはプリンタとして見える。(実際に印刷もできる)

次回はandroidからこのプリンタを探索(ディスカバリ)するコードをテストしてみよう。

Bonjour とはAppleが開発したゼロ・コンフィギュレーション技術の実装。LANにおいて、何の設定も行わず機器を使用可能にすることができる。AirPrintもこのBonjourサービスの上で動作することを前提としている。

REkit

zuccoi/REKit · GitHub

REkitはMac/iOSのFoundationクラスであるNSObjectを拡張するカテゴリで提供されており、Objective-Cの動的な機能とBlocksを活用して、Objective-Cのイディオムを拡張することができる。

  • REResponder

オブジェクトインスタンスのメソッドをランタイムにBlockで記述した内容に置き換えることができる。

  • REObserver

従来からあるKVOの機能をBlocksを使い、より簡単に書くことができる。

両者とも非常に強力で動的な機能をobjective-Cプログラマが享受できるが、特に前者のREResponderは上手く使うとAOPのように関心事をロジックから切り離すことができたり、モックやプロキシなどを生成したりと使い道次第で様々な用途に使えるのではないだろうか。

ちなみにREADMEを見ると解るが、REkitの開発者はKazki Miura氏つまり日本の方である。

OCUnitテストターゲットにて"ld: file not found"でビルドできない

ld: file not found: /Users/kazz/Library/Developer/Xcode/DerivedData/プロダクト名-eiottghqglskmzegzcmbbdbkgyhh/Build/Products/Debug-iphonesimulator/プロダクト名.app/プロダクト名
clang: error: linker command failed with exit code 1 (use -v to see invocation)

古いプロジェクトを引っ張りだして来てOCUnitテストをしようとしたらビルドが通らない。

こんな時はプロジェクトのアプリケーションターゲットとテストプロジェクトで矛盾が起きていることが多い。

具体的には、テストターゲットのBuild Setting中のBundle Loaderパラメタの値がアプリケーションターゲットのProduct Nameと矛盾しているのが原因だ。

プロジェクトを作成した後にプロダクト名を変更したりすると、なぜかハードコードされているこのパラメタはテストターゲットの設定だけが古いままになり、整合性が取れなくなってしまうことがあるのだ。

こんなことで午前中潰してしまった。とほほほ。

iOS6.0以降、GCDの'dispatch_release'はARC環境では使えない

以前GCD(Grand Central Dispatch)のエントリを書いた時に

dispatch_release(group)
グループは有限のリソースのため、使用が終わったら必ず解放する必要がある。(ARCの影響を受けないことに注意)

と書いたが、iOS 6.1のプロジェクトを新たに作成して以前のコードを取り込んだ所、以下のエラーでコンパイルが通らない

'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'

どうやらiOS6.0以降扱いが変わったらしい。
今までエラーになっていなかったのはXcodeにて"iOS Deplyment Target"を5.xにしていたために、後方互換性が働いていたのだろう。

cvBlobsLib (その2)

cvBlobsLib - OpenCV Wiki
cvBlobsLibが使えそうだということは解ったのだが、問題は主要なモバイルプラットホームで使えるかどうかだ。

iPhone(iOS)

aaronsung/cvBlobsLib-iOS · GitHub
Objective-C(++)へのポートとも言える実装。本家と同等に使えそうだ。

android

.... 残念ながら無いようだ。
同様の用途に使うライブラリィは見つかったのだが、
cvblob-for-android - cvBlob for Android - Google Project Hosting
こちらはソースコードを見るとandroid.graphicsパッケージを利用した単独のクラスで構成されており、OpenCVとの関連性は無い。

ということで、現時点でcvBlobsLibをandroidで使うには

1. 本家のソースコードをNDKでビルドしてJNI経由でandroidから呼び出す
2. 本家のソースコードandroid版のOpenCVを使って移植する

このどちらかということになる。
自分でやるならば後者だが、時間が無いなぁ。