Category Archives: Objective-c

CoreDataをActive Record風に扱えるようにするiOS用のライブラリである、MagicalRecordの導入方法についてのメモ書き

今回作成したサンプルは azu/MagicalRecord-Sample – GitHub に置いてあります。

まずはCoreDataのモデルの下準備から

1. CoreDataを使うプロジェクトを作成する(種類はなんでもいいけど、今回はEmptyプロジェクト)
プロジェクト名を MagicalRecordSample とした

NewImage

 

2. <プロジェクト名>.xcdatamodeldファイル があるので、データベースのモデルを作成する

今回は以下のようなENTITIESを一つ持ったモデルを作成した。

ENTITIES : Person
Attributes :

  • age
  • name

Xcode

3. 次に作成したPersonエンティティからmogeneratorを使ってモデルのカスタムクラスを作成する
mogenerator + Xmo’d からpkgをインストールするか、$ brew install mogenerator でインストールできる。

モデルクラスは適当にグループに分けたほうが見やすくなるので、先にModelsというグループ用のディレクトリを作っておいてる。

NewImage

 

mogeneratorをダウンロードしてインストールするとmogeneratorというコマンドがターミナルから利用できるようになる。
mogenerator -mでxcdatamodel(xcdatamodeldの中にある。紛らわしいので注意…)を指定して、-Oオプションでモデルクラスの出力先を指定する。(今回は先程作成しておいたModelsディレクトリに出力)

✗ mogenerator -m MagicalRecordSample/MagicalRecordSample.xcdatamodeld/MagicalRecordSample.xcdatamodel/ -O MagicalRecordSample/Models/

実行すると4つのPersonファイルが生成される。

この辺参照

生成されたファイルが入ったModelsディレクトリをそのままプロジェクトに追加する。

NewImage

これでモデル側の準備は終わりで、次は本題のMagicalRecordの導入方法について

MagicalRecordの導入方法

1. MagicalRecordをダウンロードする
中にあるSourceディレクトリをプロジェクトにD&Dなどで追加する。

2. <プロジェクト名>-Prefix.pch ファイルに

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <CoreData/CoreData.h>
    #import "CoreData+MagicalRecord.h"
    #define MR_SHORTHAND 1
#endif

という感じで#define MR_SHORTHANDを定義する。(これを書くと[Person findAll]みたいな書き方ができるようになる)
#import “CoreData+MagicalRecord.h”は <プロジェクト名>-Prefix.pchに書いて、MagicalRecordのヘッダファイルを読み込んでおく。

MagicalRecordの使い方

ここまでの準備が結構面倒だけど、MagicalRecordを導入しておけばCoreDataの操作がものすごく簡潔にできる。

ものすごく適当な例を書くと次のように読み書きが書ける。
README.mdに書かれてるように、最初に [MagicalRecordHelpers setup*]でデータの保存方法を指定する。(ファイルやメモリ等種類がある)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    /* MagicalRecord Sample Code */
    // sqliteファイルの保存名の設定
    [MagicalRecordHelpers setupCoreDataStackWithStoreNamed:@"MagicalRecordSample.sqlite"];
    // CoreDataのManagedObject Contextを設定する。
    NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext];

    Person *person = [Person createEntity];// エンティティを作成する
    person.name = @"hito";
    person.age = [NSDecimalNumber decimalNumberWithString:@"18"];
    [context MR_save];// 保存

    // 保存した結果を取り出して表示
    NSLog(@"MagicalRecord FindAll");
    NSArray *result = [Person findAll];
    for(id per in result) {
        NSLog(@"name : %@ | age : %d",[per name],[[per age] integerValue]);
    }
    
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

createEntityで作成したエンティティに値を設定して保存、保存したデータは[モデルクラス findAll];で読み出せる。(抽出条件も結構簡単にかけるのでものすごく楽にできる)

NewImage

にメソッドとかいろいろ載っていますが、ヘッダーファイルやユニットテストのコードを見るとわかるように全部がreadmeに書かれてるわけではないです。
正直、自分もまだわからない部分が多いので、もっとCoreDataに詳しい方が記事を書いてくれることを期待します。
(今回全く書いてないけど、Blocksを使った書き方もできるのでかなりスッキリと書ける。)

参考

JavaScript Advent Calendar 2011 (フレームワークコース) 21日目の記事です。

Sencha Touchを初めて使った時の話で、あまり実用的な内容ではないですが、
これからSencha Touchを使う人に何かしら参考になればと思います。

Sench TouchでiPadモック

Sencha Touchとは…という話は恐らく聞いたことがある人は多いと思うので省きますが、
iOSとAndroid(要はWebkit系)をサポートしたスマートフォン向けのWebアプリケーションフレームワークです。

最近はObjective-CでiPhoneアプリを書いている事が多いのですが、
今回、問診票iPadアプリを作成する事になり、それ向けに動くモックが一週間後ぐらいに必要となりました。
まだiPad向けのアプリは作成した事はなかったので時間的な心配と、 Sencha Touch ちょっと触ってみた所感などを読んでSencha Touchが気になっていたので、
Sencha Touchでモックを作って見ることにしました。

Sencha Touchを初めて3日ほどかかって、かろうじて動く感じのモックができました。

  • http://azu.github.com/SenchTouchiPadMock/www/
    Webkit系のブラウザならiOSじゃなくても見られます
    全然Sencha Touchのルールに則ってない書き方をしてるのでソースは参考にしてはいけない

NewImage

iPadアプリでいうUISplitViewController な感じで、それぞれの項目に入力欄や選択肢が出るような感じのモックです。

NewImage

デザイン的なモックでは無いので、フォントサイズを自由に変更できるスライダーを載せて、サイズをいじりながら見られるようにしていました。

NewImage

このSencha Touchの入力部品(テキストエリアやチェックボックス)は、以下のようなJSONオブジェクト的なものを書くだけで作成できるので、かなり手間を省いてできるのでとりあえず並べるものを作るにはとても便利です。入れ子のような構造もちゃんとUIに反映されるのでかなり便利でした。

var data = [{
        title:"年齢",
        instructions:"あなたの年齢を入力してください",
        xtype:'fieldset',
        id:'yearsold',
        items:[
            {
                xtype:"textfield",
                inputType:"number",
                name:'yearsold',
                label:'年齢',
                placeholder:"年齢を入力"
            }
        ]
    },
    {
        title:"一日に吸うタバコの本数",
        instructions:"一日にタバコを吸う本数(禁煙中ならば、以前は何本くらいすっていましたか?)
" + "今まで、合計で何年間くらいタバコを吸っていましたか?", xtype:'fieldset', id:'cigarettes', items:[ { xtype:"textfield", inputType:"number", label:"一日の喫煙本数", name:"cigarettesPerDay" }, { xtype:"textfield", inputType:"number", label:"喫煙年数", name:"yearsOfSmoking" } ] }, { title:"体重・身長", instructions:"あなたの体重と身長を入力してください", xtype:'fieldset', items:[ { xtype:"textfield", inputType:"number", label:"体重", name:"bodyWeight", placeHolder:"体重を入力" }, { xtype:"textfield", inputType:"number", label:"身長", name:"bodyHeight", placeHolder:"身長を入力" } ] }, { title:"チェックシート", instructions:"それぞれの項目に対して該当するものをチェックしてください", xtype:'container', cls:'checksheet', // heightがたりないので無理やり100%に id:'checksheet', scroll:'vertical', items:[ // それぞれの問題 { title:"天候により、せきがひどくなることがありますか?", xtype:'fieldset', defaultType:"radiofield", items:[ { name:'tmp_seki', label:'はい(天候によりひどくなる)', value:'3' }, { name:'tmp_seki', label:'いいえ(天候は関係ない)', value:'0' }, { name:'tmp_seki', label:"せきは出ない", value:'0' } ] }, { title:"風邪をひいていないのにたんが絡むことがありますか?", xtype:'fieldset', defaultType:"radiofield", items:[ { name:'kaze_tan', label:'はい', value:'3' }, { name:'kaze_tan', label:'いいえ', value:'0' } ] }, { title:"朝起きてすぐにたんがカラムことがよくありますか?", xtype:'fieldset', defaultType:"radiofield", items:[ { name:'tan_karamu', label:'はい', value:'0' }, { name:'tan_karamu', label:'いいえ', value:'3' } ] }, { title:"喘鳴(ゼイゼイ、ヒューヒュー)がよくありますか?", xtype:'fieldset', defaultType:"radiofield", items:[ { name:'zenmei', label:'いいえ、ありません', value:'0' }, { name:'zenmei', label:'時々、もしくはよくある', value:'4' } ] }, { title:"今現在(もしくは今まで)アレルギーの症状はありますか?", xtype:'fieldset', defaultType:"radiofield", items:[ { name:'allergy', label:'はい', value:'0' }, { name:'allergy', label:'いいえ', value:'3' } ] }, ] } ];

モックからネイティブアプリに

上記のSencha Touchで作成したモックも参考にしつつ、Objective-CでネイティブのiPadアプリとして作りなおしています。
まだ作成中な感じですが、なんとなく雰囲気だけ残っています…

ネイティブの場合も同じように、JSONで質問項目やメタデータの一覧を書いて、そのJSONを元にUIを作るコード書いて作成しました。
Sencha Touchの場合だと既にJSONからUI(フォーム系)を作るものが用意されているので、この辺がかなり簡略化できていたんだなとObjective-Cで書いてて思いました。

NewImage

NewImage

 

Sencha Touchの感想

Sencha TouchはMVCなど書き方に一見つかみにくい要素があるので少し学習する時間がかかると思いますが、
その辺が上手くつかめるようになれば、jQuery Mobileなどよりもいろいろな事がやりやすかったり、ガッチリしたものも作成できると思います。

Sencha Touch2ではもう少しコードがすっきりしたり、PhoneGapのようなネイティブアプリへのパッケージング機能を持ったり、
よりスマートフォン向けアプリが書きやすくなると思うので、触る機会が増えるかもしれません。

今回のモックを作るにおいて参考したサイトなど

Sencha Touchのインターフェースのレイアウトについてのスクリーンキャスト。
layoutの値による違いを視覚的に把握できるので見ておくべき。
SenchはLearn Sencha Touch | Learn | Senchaなどにスクリーンキャスト等がいろいろあるが、
この中でも特にDrew Neilによるスクリーンキャストはとても解りやすくできているため見ておいて損はない。
スクリーンキャストとしての出来がとても良い感じ Learn Sencha Touch w/ Drew Neil on Vimeo

まだ発売前の書籍ですが、サンプルコードも置かれているのでコンポーネントの表示を確認するのに便利。
ただ、実際のコードはExt.setupなど最低限な構成なため実際に使うときはコピペなどをしないほうがいいと思います。
(後、ライブラリのパスとかがまちまちなので自分で調整しないとまともに見えないサンプルも多い) 
サンプルで公開されているIntroducing Sencha Touch (Green Paper – PDF)には、Sencha Touchにおける構成部品の名前などの図が乗っているので、結構参考になる。

Sencha Touchを使ってノートアプリの作成チュートリアル
モックデザインなど図も多くあり、かなり細かく解説している。(書籍も出してるみたい)

その他の参考になるもの

iOSアプリでPrivate APIが使用されてないかチェックしたり、ビルドしたものをいろいろと調べるツールの紹介です。

基本的には、iOSアプリのバイナリの中身を見たりするアプリなので、
まずはバイナリを手元においておく必要があります。

開発してるアプリのプロジェクトをXCodeで開いてArchiveから、OrganizerでShareからipaファイルを出力します。

NewImage

ipaファイルはzipファイルなので、拡張子をzipにするなどして解凍すると、中にアプリ名.appのフォルダがあるので、
その中に、アプリ名(拡張子なし)のファイルがバイナリファイルです

sample というアプリなら、sample.ipaを解凍してsampleディレクトリができるので、
sample/Payload/sample.app/sample が対象のバイナリファイルとなっています。

ツール紹介

バイナリの内容を表示できるコマンドラインツール(元々入ってる)で、リンクしているライブラリ一覧やディスアセンブルしたデータを表示する事ができます。
otoolでバイナリの内容をいろいろと調べる方法 – Over&Out その後

  • nm

こちらも元から入ってるコマンドラインツールで、リンクしているシンボルテーブルの一覧を表示できます。

  • strings

これも同様で、オブジェクトやバイナリに含まれているObjective-Cのセレクタを一覧表示できます。

例えば、テスティングライブラリのgh-unitはapp storeに申請するリリースビルドに含めてしまうと、
_terminateWithStatus というPrivate APIを利用しているという事になってしまいリジェクトされます。

実際にGHUnitIOS.frameworkのGHUnitIOS.framework/Versions/Current/GHUnitIOS(バイナリ) に対してstringsコマンドで
_terminateWithStatus:メソッドが使われている事が確認できます。

➜  Current  strings GHUnitIOS | grep "_terminateWithStatus"
_terminateWithStatus:

このように、Private APIを利用していないかのチェックにも使えます。
(アプリの場合は後述するApp Scannerでもよさそう)

otoolが表示するような内容をGUIから利用できるMachOViewというソフトウェアも存在します。 
これらのツールについては下記が参考になります。

 

App ScannerはiOSアプリを対象にPrivate APIが使用されていないかをチェックできるソフトウェアです。
Stringsコマンドのように、Objective-Cセレクタの検索をGUIから出来たりするので便利です。

ただし、Private APIのチェックは完全ではないのとlinkしてるフレームワークの中身までちゃんとチェックされてるのか、
よくわからない部分もあるため、あまり過信しすぎないようにしましょう。

Private APIでリジェクトされた場合も“Layout”キーワードによってiOS版アプリがRejectされる件につきまして « Appcelerator Developer Centerアプリの審査でリジェクトされてイラッときたこと | HMDT Blogなどのような場合もあるので、Private APIの定義が難しいですが、このようなツールは便利です。

UIWebViewやASIHTTPRequestではNSHTTPCookieStorageが管理しているクッキーを共有して使うことができる(逆に使わない設定も可能)ので、
起動中はセッションの設定などでクッキーのやり取りを気にしなくてもいいので便利なのですが、さすがに永続化は自動ではやりません
そのため、アプリを再起動してもUIWebVIewで表示するサイトのログイン状態を継続したい場合等はNSHTTPCookieStorageからクッキーを取り出して保存して置く必要があります。

NSUserDefaultsを使い単純にファイルとして保存することを考えた場合、クッキーが変化するごとに(iOSだとそもそもキャッチできなさそう)保存するのはI/Oが足を引っ張りそうなので、
起動時と終了時にそれぞれ読み込み、保存処理を行うようにします。
AppDelegateクラスのdelagateメソッドを使ってこれを実装します。

マルチタスクが有効なiOSだと、applicationWillTerminateが呼ばれない事も多いのでapplicationDidEnterBackgroundのタイミングでも保存処理を行うようにしています。

参考

GH-Unit導入方法とAppCodeを使ったコンソール実行についての動画です。
(画質が悪くてとても見にくいです…)

GHUnitの導入については下記の記事の手順そのままです。

後半は(上記の設定と一緒にしていますが)、GH-Unitをコマンドラインから実行できるようにして、動画ではAppCodeから呼び出して実行結果をAppCode内のコンソールで見るという感じの動画になっています。

コマンドラインから実行させる手順について動画の補足をしておきます。
コマンドライン用の設定はguide_command_line Documentに書かれているので、これをよく読んでおくと動画やっていることがわかると思います。

箇条書きで手順を書くと

  1. GH-Unitをアプリとして実行できるようにプロジェクトの設定をしておく
  2. 最初にcloneしてきたレポジトリ内にあるRunTests.shを、プロジェクトファイルと同じディレクトリに置く
  3. Build Phasesで、Add Run Scriptから先ほどのRunTests.shを実行するため
    sh RunTests.sh

    と設定する。

  4. コマンドラインからビルドコマンドを叩いて実行する

コマンドライン用の設定はこれだけで、後はguide_command_line Documentを見てわかるように、コマンドラインから叩くだけ実行できるようになります。
iOS向けのプロジェクトの場合は以下のコマンドをプロジェクトファイルと同じディレクトリでコマンドラインから入力する事で、テスト結果を表示できます。


GHUNIT_CLI=1 xcodebuild -target {{Tests}} -configuration Debug -sdk iphonesimulator build

{{Tests}}の部分は各自プロジェクトでGH-Unitのテストコードを置いてあるTarget名を入力します。(使いまわせるようにTestsなどに固定したほうが楽かもしれません)
レポジトリにもサンプルMakefileが入っていますが、実際にCIなどで回す場合はMakefileのようなものを書いたほうが楽になると思います
動画ではGHUnit-CLI.shというファイルに上記のコマンドを書いて、AppCodeのExternal Toolsから呼び出してAppCodeのコンソールに表示させています。

この動画でやっている事自体は余り実用性は無いですが、GH-Unitはコマンドラインでヘッドレステストも行えることがわかったかと思います。
コマンドラインでの実行結果を処理するスクリプト等を書けば、GH-Unitをもっと便利に使えるようになる気がします。
実際にJenkinsなどでCI していく場合には、自分の撮影したものではないですが以下の動画が参考になると思います。