実機での自動実行を行う xcodebuildコマンドのパラメータ設定

iOS 開発でもテスト自動化が流行っているようですね。
というわけで、テストコードを実機で動かすためのコマンドを調べました。
シミュレータ上で自動実行するパラメータ設定はいろいろ紹介されているようですが、
実機での自動実行を行うパラメータ設定を見つけることが出来ず、少し苦労したのでメモ。

最初からxcodebuild(1) Mac OS X Developer Tools Manual Pageを参照しろとの話もありますが…

以下のシェルスクリプトで実機でのテストコード自動実行ができます。

1
2
3
4
5
6
7
8
9
10
11
#! /bin/sh
 
# iPhone (iOS 実機)
PROJECT="(プロジェクト名).xcodeproj"
SCHEME="(スキーマ名)"
DESTINATION="platform=iOS,name=(実機の名前。省略可。),id=(識別子 UDID)"
 
xcodebuild -project ${PROJECT} \
     -scheme ${SCHEME} \
     -destination "${DESTINATION}" \
     test

また、パラメータで指定されている”スキーマ名”、”Destination”は以下の画面で確認できる。

xcodeでのスキーマ名、Destination の確認方法

xcodeでのスキーマ名、Destination の確認方法

久しぶりに UITableView のコードを書いて忘れていた点をメモ

UITableView のセルを制御するために色々なインデックス値などを保持しているコードをたまに見かけますが、できる限り余計なインデックス値などはメンバ変数として保持したくないものです。
コードで取得できるインデックスはコードで取得できた方が良いでしょう。
以下のメソッド忘れてました。(>_<)

1
[self.tableView indexPathForCell:cell];

参考: UITableViewCell 上に配置した UIControl のアクションから indexPath を取得する

あと、UITableViewCell に addSubview する場合は、

1
[cell.contentView addSubview:button];

を使用しましょう。

参考: Custome UITableViewCellの[self addSubview]と[self.contentView addView]の違い

久しぶりにコード書いて忘れてたので、メモです。

ナビゲーションバーもビューで覆う方法

ビューを全画面に表示しようとして、ナビゲーションバー上までビューが覆い被せることができず少し迷った。
window に addSubview すれば以下の画面のようにナビゲーションバー上までビューを覆い被せることが出来る。

1
2
3
4
5
6
LockViewController *lockView;
lockView = [[LockViewController alloc] init];
lockView.view.frame = CGRectMake(0, 0, 320, 480);
 
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.window addSubview:lockView.view];

iPhone

OS X 10.8.4 アップデート後 xcode シミュレータ実行時エラーの対処方法

OS X 10.8.4 にしてから、xcode からシミュレータ実行した際にエラーが出力されて困っていたが、一時的な対処方法が紹介されていた。
再実行 (⌘+R) でエラーでまくりで困ってたんですよ。(^^)

とりあえず、以下の記事の内容の通り、デバッガを LLDB から GDB に変更。

参照:OS X 10.8.4でXcodeでiOS Simulatorを実行したときにSIGABRTでアプリがクラッシュすることがある問題の暫定的な対処

追記: 6/14 Xcode 4.6.3 がリリースされており、問題も対処されているようです。

CoreData データベースにマスタデータを取り込む方法

iOS アプリで SQLite のマスタデータをデータベースに用意することに少し苦労したので、メモしておく。

iOS アプリで生成した SQLite データベースのテーブル構成を SQLite database browser で確認した。

・CoreData のテーブルには、テーブル名に"Z"が付加されている
・テーブル カラムにも"Z_PK", "Z_ENT", "Z_OPT" などの制御カラム(?)が追加されている

上記制御カラムの生成ルールがよくわからなかったので、CoreData データベースへのデータ追加は CoreData プログラムコードで行うこととした。
(他にいい方法はないのものか?)

1. 自作のCSVデータ取り込みアプリ(CoreData)でデータをインポート

CSVデータ取り込みアプリの .xcdatamodel にマスタテーブルを定義する
マスタテーブルへデータを取り込めるように改変する
マスタ データを取り込む

2. アプリで利用するSQLiteデータベースへデータをコピー

1
2
3
4
5
6
7
8
$ sqlite3 アプリのデータベース ファイル名.sqlite
sqlite> attach database 'マスタを保持したデータベース ファイル名.sqlite' as old;
sqlite> .database
sqlite> begin;
sqlite> insert into main.テーブル名('Z_ENT', 'Z_OPT', …カラム名) select Z_ENT, Z_OPT, …カラム名 from old.テーブル名; 
注意点: Z_PK などのプライマリキーは指定できない
sqlite> commit;
sqlite> .exit

3. NSBundle からデータベースをコピー

マスタデータを含むデータベースを xcode プロジェクトに追加しておき、アプリ起動時に以下のコードでコピーする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma mark - Application's documents directory
 
// アプリのドキュメント ディレクトリ パスを返す
- (NSString *)applicationDocumentsDirectory
{    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    return basePath;
}
 
- (void)setupSqliteDb
{
    // SQLiteデータベース を保存するパスを取得する
    NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"BikeMaintenance.sqlite"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
 
    // SQLiteデータベースが存在しない場合、作成する
    if (![fileManager fileExistsAtPath:storePath]) {
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"BikeMaintenance" ofType:@"sqlite"];
        if (defaultStorePath) {
            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
        }
    }
}

サウンド ファイルのフォーマットを変換する方法

(コマンド書式)
/usr/bin/afconvert -f ファイルフォーマット -d コーデック [入力ファイル] [出力ファイル]

wav → caf 形式への変換方法
$ afconvert -f caff -d LEI16 input.wav output.caf

mp3 → caf 形式への変換方法
$ afconvert -f caff -d ima4 input.mp3 output.caf

フォーマット一覧を参照する
$ afconvert -hf

MagicalRecord を使用する方法

xcode 4.6.2 にて確認した。
xcode ってバージョンが変わると操作手順が変わることがあり、久しぶりに操作するときれいさっぱり忘れていることがあるので、メモ。

1. <プロジェクト名>.xcdatamodeld ファイルを作成する

2. CoreData エンティティを基にモデルクラス ファイルを作成する (( エンティティ名).h / .m)
NSManagedObject

3. mogenerator でカスタム クラス ファイルを作成する (_(エンティティ名).h / .m)
上記2.で作成したクラスを基に _”アンダーバー”が付加されたファイルが出力される。
出力されたファイルはプロジェクトに追加する。
(例) aaa.h → _aaa.h ファイルが作成される

Model ディレクトリに mogenerator が作成するファイルを出力する場合の例 (ARC対応)

1
2
3
mogenerator --template-var arc=true -m Model/(プロジェクト名).xcdatamodeld/(プロジェクト名).xcdatamodel/ -O Model/
 
(ARC対応しない場合は、--template-var arc=true は不要)

4. プリコンパイル ヘッダに MagicalRecord のヘッダを追加する

1
2
3
4
5
6
7
8
#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
 
    // MagicalRecord 用
    #define MR_SHORTHAND 1
    #import "CoreData+MagicalRecord.h"
#endif

5. CoreData.framework を追加しておく

参照: CoreDataをActiveRecord風に扱えるMagicalRecordの使い方

iOS6 で推奨されてなくなっているメソッド

deprecatedなメソッドがあるようなので、気づいたタイミングで対処。
モーダルビュー画面遷移のコードが推奨されなくなっていたので、新しいメソッドを利用するようにした。

1
2
3
4
5
6
7
 'presentModalViewController:animated:' is deprecated: first deprecated in iOS 6.0
 
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion NS_AVAILABLE_IOS(5_0);
 
[self presentViewController:(ビューコントローラ) animated:YES completion: nil];
[self dismissViewControllerAnimated:YES completion:NULL];

“Application windows are expected to have a root view controller at the end of application launch” がログに出力された場合の対処方法

iOS 4 以降は、UIWindow の rootViewController プロパティで、ビューコントローラを直接指定する方法が推奨されているとのこと。
iOS 6 SDK で以前の方法を使うと、画面の回転が正しく効かないなど不具合の原因となるらしい。

1
2
3
4
5
6
7
8
9
10
11
// iOS 6 以降の推奨
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
 :
(省略)
 :
    self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:(UIViewController を指定する)];
 
    [self.window makeKeyAndVisible];
    return YES;
}

参照: 既存プロジェクトの iOS 6 SDK 移行