職人が教える!iOSアプリ開発で使いこなしたいとっておきのOSS

第3回 実装の面倒な部分をうまくラップしてくれている便利カテゴリ3選

この記事を読むのに必要な時間:およそ 6 分

面倒なデリゲート処理を Blocks で記述できるカテゴリ

たとえば,UIAlertViewやUIActionSheetのデリゲート処理の記述を面倒に感じている方は多いのではないでしょうか。

  • プロトコルへの準拠を宣言する
  • delegate プロパティに値をセット
  • 表示処理を実装
  • デリゲートメソッドを実装

書き出してみるとこれだけのことではあるのですが,それぞれ書く場所が違い,メソッドも増えるのでやはり煩雑さは否めません。

感じるところは皆同じらしく,この UIAlertView, UIIActionSheet のデリゲート処理を Blocks で記述できるようにしたカテゴリやサブクラスがオープンソースで数多く公開されています。

たとえば UIKitCategoryAdditions というカテゴリを使用すると,上記の手順が

  • 表示処理を実装

これだけになります。表示処理自体もオブジェクトの生成や表示メソッドのコールをラップしてくれているので,下記のように1メソッドで済みます。

[UIActionSheet actionSheetWithTitle:@"Sample" 
                            message:@"Hello World" 
             destructiveButtonTitle:@"Cancel" 
                            buttons:[NSArray arrayWithObjects:@"First", @"Second", nil]
                         showInView:self.view 
                          onDismiss:^(int buttonIndex) {
                              // キャンセル以外のボタンが押された場合の処理
                              NSLog(@"%d", buttonIndex);
                          }
                           onCancel:^ {
                               // キャンセルボタンが押された場合の処理
                               NSLog(@"Cancelled");
                           }];

また,UIImagePickerControllerを用いて画像選択UIを実装する際には,UIActionSheetで画像ソースをアルバムにするかカメラにするかを選択させてからUIImagePickerControllerを表示し,UIImagePickerControllerDelegateのデリゲートメソッドで画像選択後の処理を行う,というフローが一般的によく用いられます。

この一連のフローを実装するとそこそこのステップ数になりますが,UIKitCategoryAdditionsを用いれば,1つのメソッドをコールするだけで済むようになります。

[UIActionSheet photoPickerWithTitle:@"Choose an image" 
                         showInView:self.view 
                          presentVC:self 
                      onPhotoPicked:^(UIImage* image) {
                          // 画像が選択された場合の処理
                          NSLog(@"%@", image);
                      }
                           onCancel:^ {
                               // キャンセルされた場合の処理
                               NSLog(@"Cancelled"); 
                           }];

……と,ここまでUIKitCategoryAdditionsを推しましたが,実はソース内部の実装を見るとあまり宜しくない点も見受けられます。

static DismissBlock _dismissBlock;
static CancelBlock _cancelBlock;
            
+ (void) actionSheetWithTitle:(NSString*) title                     
                      message:(NSString*) message          
       destructiveButtonTitle:(NSString*) destructiveButtonTitle
                      buttons:(NSArray*) buttonTitles
                   showInView:(UIView*) view
                    onDismiss:(DismissBlock) dismissed                   
                     onCancel:(CancelBlock) cancelled
{
    [_cancelBlock release];
    _cancelBlock  = [cancelled copy];
    
    [_dismissBlock release];
    _dismissBlock  = [dismissed copy];
    
    // 後略

このようにstatic変数にBlockをcopyして保持しており,直前でreleaseしているものの,最後に使用したBlockの参照はずっと残っていることになります。インスタンス変数に保持するか,使い終わった後にBlockを解放するかの修正を行って使用した方がベターです。

インスタンス変数に保持するようにした場合,現状クラスメソッドとなっているものをインスタンスメソッドにする必要があり,せっかくのシンプルなAPIに手を入れることになりますので,筆者は後者の方法(使い終わった後にBlockを解放)でforkしました。forkしたリポジトリのURLは下記になります。

このUIKitCategoryAdditionsだけでなく,Blockオブジェクトの保持はどのBlocks関連ライブラリにもついてまわるので,使用する際には「Blockをどのように保持しているか?」⁠ちゃんとcopyしているか,必要なくなったら解放しているか)についてご注意ください。

そして,Blocksを使用したライブラリを選ぶ際の注意点がもう一点あります。⁠循環参照」です。

Block内でそのBlockを保持するオブジェクト自身を強参照すると,循環参照になってしまいます。たとえばselfは__strong修飾子付きid型変数なので,Block内でselfを使用している場合は循環参照になっている可能性があります。そのような場合は,下記のように__weak修飾子を付けた変数に代入することで回避可能です。

id __weak hoge = self;

上で紹介したUIKitCategoryAdditionsにも,Blocksを使用したカテゴリとしては,遅延実行をBlocksで記述できるようにしたもの,CAAnimationのアニメーション終了後の処理を Blocksで記述できるようにしたもの,KVOのオブザーバの処理をBlocksで記述できるようにしたもの,NSTimer+Blocks等々,数多く便利なものが公開されているのですが,上記のようなBlocksの注意点を踏まえ現時点で最もお勧めできるのは,BlocksKitです。

有名なOSSなので利用者も多く,更新やforkも比較的頻繁に行われているため信頼性は他の類似ライブラリより高いといえます。何よりもBlocksの応用で考え得る便利な拡張機能をだいたい備えており,その名の通りBlocksを使用したライブラリの集大成のようなOSSとなっています。

日本語での解説記事もいくつかあるので本連載では解説は省略しますが,とても便利なライブラリなのでぜひお試しください。

英単語の単数形/複数形変換をメソッド1つで行えるようにするNSStringのカテゴリ

iOSアプリを英語ローカライズする際,単数形/複数形の取り扱いはなかなかやっかいです。-s, -es の違いを始めとして,不可算名詞,person/peopleのようにイレギュラーなものもあります。

それら諸々のルールをプロパティリストファイルに記述し,

- (NSString *)pluralizeString;
- (NSString *)singularizeString;

このように非常にシンプルなメソッドで単数/複数変換を行えるようにしたカテゴリがActiveSupportInflectorです。

ダウンロード先は下記になります(MITライセンス⁠⁠。

使用にあたってはNSString+ActiveSupportInflectorクラス以外に,ActiveSupportInflectorクラスとActiveSupportInflector.plistをプロジェクトに追加する必要があります。

ちなみに,上記リンク先はforkした筆者のリポジトリになるのですが,修正点としては下記のようにNSString+ActiveSupportInflector.mを1行コメントアウトしてあります。

//#import "NSString+MSAdditions.h"

このNSString+MSAdditions.hというファイルはリポジトリに存在していないのですが,そもそもこれに依存したコードになっていないので,コメントアウトすることで問題なくビルドできるようになります。

このカテゴリによってどんな変換ができるようになるかは,リポジトリ内に付随しているテストケース用のプロパティリストファイルActiveSupportInflectorTest.plistを見ると一目瞭然ですので,使用を検討されている方はチラッとのぞいてみてください。以下に一部抜粋しておきます。

("search", "searches"),
("stack", "stacks"),
("fish", "fish"),
("category", "categories"),
("ability", "abilities"),
("agency", "agencies"),
("index", "indices"),
("wife", "wives"),
("safe", "saves"),
("half", "halves"),
("person", "people"),
("man", "men"),
("woman", "women"),
("basis", "bases"),

まとめ

UIImageに各種画像処理機能を持たせるNYXImagesKit,UIAlertViewやUIActionSheetのデリゲート処理をBlocksで記述できるようにするUIKitCategoryAdditions,NSStringに単数形/複数形変換機能を持たせるActiveSupportInflectorの3つのカテゴリを紹介しました。

今回は記事の都合上3つしか紹介できませんでしたが,まだまだオープンソースの便利なカテゴリは数多く存在しますので,また別の回で紹介したいと思います。

著者プロフィール

堤修一(つつみしゅういち)

1978年生まれ。京都大学工学部を卒業後,同大学院修了。その後,NTTデータにて音声認識技術の研究開発,キヤノンにて画像処理機能の設計に携わる。

2010年より面白法人カヤックに入社。3年間ほぼiOSアプリ開発に専念し,フルスクラッチで開発しリリースしたアプリは30本以上。代表作は150万ユーザを突破した「バウンドモンスターズ」,AppStore Best of 2012を獲得した「タップ忍者」,カンヌ国際広告祭でブロンズを獲得した「Domino's App」など。

現在は,米国シリコンバレーのマウンテンビューにあるAppSocially社の一員として活躍中。

著書=『iOSアプリ開発 達人のレシピ100―開発現場で実証された実用コード集』

ブログ=Over&Outその後

Github=shu223

Twitter=shu223