Xcodeで1つのプロジェクトから無料版と有料版のiOSアプリをビルドする
iOSアプリの無料版と有料版を提供するとき等に、Xcodeの1つのプロジェクトから無料版と有料版の2つのアプリをビルドしたいケースがあります。
Xcodeで2つのプロジェクトに分けてしまうと、バグ修正や新機能追加のたびに両プロジェクトをコーディングする必要があって大変なので、広告や有無や制限値の差異程度であれば1つのプロジェクトから2つのアプリをビルドすると楽です。
ここでは1つのプロジェクトの同一ソースから2つのアプリをビルドする方法を説明します。
Xcode上での作業
ここでは、有料版が既にあって、新たに無料版を追加する場合を例にして説明します。
有料版のTargetを複製して無料版を作る
- Projectツリーを開く
- Targets > 有料版アプリを右クリックする
- Duplicateをクリックして複製する
- 無料版のTarget名が「Target名 copy」となっているので無料版の名前に修正する
- 無料版のTarget > General > Identity > Bundle Identifierを無料版のものに修正する
無料版のプロダクト名を設定する
- 無料版のTarget > Build Settings > Packaging > Product Name
- Product Nameが「Target名 copy」となっているので無料版の名前に修正する

無料版のビルド時のマクロを設定する
- 無料版のTarget > Build Settings -> Apple LLVM 5.X - Preprocessing
Preprocessor Macrosの
DebugとRelease両方に無料版のビルドフラグを追加するFREE_VERSION=1

コードに
#ifdef無料版と有料版の処理を分岐させる#ifdef FREE_VERSION 無料版の処理 #elif 有料版の処理 #endif※注意 無料版/有料版のコードを分岐させるには以下のようなパターンがあります。
- シングルトンオブジェクトを作って、一度
#ifdef内で有料/無料フラグを設定して、それ以降はif文で分岐する #ifdefで関数内で処理を分岐する#ifdefで無料版の関数と有料版の関数をわける
無料版にしか存在しない広告系の関数などは3の方法で消すのがよいです。一方で、関数中のしきい値が異なる程度なら1や2の方法で分岐するのがよいかもしれません。
#ifdefマクロは強力で便利ですが、Xcodeのシンタックスハイライトを崩すケースや無料/有料両方のコード修正が必要になるケースやリファクタリング機能がうまく使えなくなるケースなど様々なデメリットもあるため、メンテナンスコストを考えて使うと良いと思います。- シングルトンオブジェクトを作って、一度
MagicalRecord に mogenerator を組み合わせてよいか
CoreDataを使う際に、MagicalRecordに加えてmogeneratorを使う事を推奨しているブログ記事が散見されますが、mogeneratorには以下のようにリジェクトのリスクがあるようです。
Don't use mogenerator methods in place of standard Core Data calls #569
Referenced non-public APIs which caused AppStore rejection. #74
筆者はmogeneratorを使っていた事もありましたが、現在は別の理由もあってMagicalRecord単体で使っています。リジェクトのリスクはmogeneratorに限った話しではないですが、注意した方がいいかもしれません。
以上
MagicalRecordでCoreDataをお手軽に使う
iOSアプリでデータを保存(serialize/deserialize)する方法としてCoreDataがありますが、コード量が多く慣れるまでにも時間がかかります。
ここではより簡単なMagicalRecordによって、CoreDataを使ってみます。
MagicalRecordはActive Recordパターンを採用したCoreDataのラッパーです。ActiveRecordは、Martin FowlerらのPatterns of Enterprise Application Architectureで名付けられ、Ruby on Railsで普及しました。
MagicalRecordがあれば、Webアプリ開発者でもCoreDataを簡単に扱えるようになり、iOSアプリ開発がより身近に感じられるようになると思います。
前提
CocoaPodsはインストール済みとします。CocoaPodsのインストール方法は以下を参照してください。
iOSアプリのプロジェクト作成
Xcodeを開いて新規プロジェクトを作成します。
File > New > Project- Use Core Dataの指定は不要です。
- 指定すると不要なコードが生成されるため、指定しない事をおすすめします。
- Use Core Dataの指定は不要です。
MagicalRecordのインストール
Podfile作成
.xcodeprojがあるディレクトリにPodfileファイルを作成します。Podfileに次の1行を追記します。pod 'MagicalRecord'
MagicalRecordインストール
ターミナル上で
Podfileと同じディレクトリに移動して以下を実行します。pod install
設定
アプリ名/アプリ名-Prefix.pchを開いて次の1行を追記します。#import "CoreData+MagicalRecord.h"
CoreDataデータモデルの定義と作成
Data Modelを追加
- メニューから
File > New > File > Data Modelを選択します。 Model.xcdatamodeldを作成します。
- メニューから
Entityを追加
- ツリー上で
Model.xcdatamodeldを選択します。 Add EntityをクリックしてEntityを追加します。- Entityを適切な名前に変更します。
- Entityの属性(Attributes)や関係(Relationship)を定義します。
- ツリー上で
Entityから必要なソースコードを自動生成
- ツリー上で
Model.xcdatamodeldを選択します。 メニューから
Editor > Create NSManagedObject Subclass > Next > Next > Createを選択します。※
Use scalar properties for priitive data typesをチェックすると以下のようなスカラー型(定数型)のプロパティがプリミティブなデータ型に変換されます。チェック不要で構いません。- Date -> NSTimeInterval.
- Double -> double.
- Float -> float.
- Integer 16/32/64 -> int16_t/int32_t/int64_t
- Boolean -> BOOL
- ツリー上で
MagicalRecordの起動/終了処理
起動処理
AppDelegete.mファイルを開いてdidFinishLaunchingWithOptionsに起動処理を追加します。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 以下を追加 [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"database_name.sqlite"]; return YES; }初期化には以下のようなメソッドが用意されています。目的に応じて選択します。
ローカルのSQLite用
+ (void) setupCoreDataStack; + (void) setupAutoMigratingCoreDataStack; + (void) setupCoreDataStackWithInMemoryStore; + (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName; + (void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;+ (void) setupCoreDataStackWithiCloudContainer:(NSString *)icloudBucket localStoreNamed:(NSString *)localStore; + (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent; + (void) setupCoreDataStackWithiCloudContainer:(NSString *)containerID contentNameKey:(NSString *)contentNameKey localStoreNamed:(NSString *)localStoreName cloudStorePathComponent:(NSString *)pathSubcomponent completion:(void(^)(void))completion;終了処理
AppDelegete.mファイルを開いてapplicationWillTerminateに終了処理を追加します。- (void)applicationWillTerminate:(UIApplication *)application { // 以下を追加 [MagicalRecord cleanUp]; }
MagicalRecordでのデータ保存
同期
Album *album = [Album MR_createEntity]; album.title = @"Friends"; : [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
※MR_saveメソッドは次期メジャーリリースの3.0からは無くなるので同じ機能を持つMR_saveToPersistentStoreAndWaitを使いましょう。
非同期
Album *album = [Album MR_createEntity]; [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ //保存前の処理(保存したいインスタンスの作成など) Album *localAlbum = [album MR_inContext:localContext]; localAlbum.title = @"NeverMind"; : } completion:^(BOOL success, NSError *error) { //保存後の処理(全件取得や件数カウントなど) }];その他
MagicalRecordでのデータ取得
全件取得
NSArray *albums = [Album findAll];条件を指定して取得
NSArray *albums = [Album MR_findByAttribute:@"genre" withValue:@"piano trio" andOrderBy:@"year" ascending:YES];その他高度な取得
MagicalRecordでのデータ更新
データ取得&更新
あらかじめAlbumのインスタンス(ここではcheckyourhead)を取得しておきます。
Album *checkyourhead = ...; checkyourhead.rate = [NSNumber numberWithInt:5]; [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
MagicalRecordでのデータ削除
全件削除
[Album MR_truncateAll]; [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];1件削除
あらかじめAlbumのインスタンス(ここではmellowgold)を取得しておきます。
Album *mellowgold = ...; [mellowgold MR_deleteEntity];
MagicalRecordでのデータカウント
カウント
NSNumber *count = [Album MR_numberOfEntities];
注意
MagicalRecordでデータの保存、更新、削除を反映する際は必ず以下を実行します。
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
以上です。MagicalRecordを使うとCoreDataを簡単に扱えます。
CocoaPodsでiOSのライブラリを簡単にインストールする
Objective-Cのライブラリを自動的にインストールするためのツールです。node.jsにおけるnpmやRubyにおけるRubyGemsと似たようなパッケージ管理ツールです。
CocoaPodインストール
$ gem install cocoapods
$ pod setup
iOSプロジェクトへのライブラリインストール
TouchDB (CouchDB互換の組込用NoSQL) をiOS開発で使ってみる
TouchDBとは
概要
公式サイトを見ると次のように解説されています。
TouchDB is a lightweight Apache CouchDB-compatible database engine suitable for embedding into mobile or desktop apps. Think of it this way: If CouchDB is MySQL, then TouchDB is SQLite.
訳: TouchDBはlightweightなApach CouchDB互換のDBエンジンで、モバイルやデスクトップアプリへの組み込みに適しています。CouchDBをMySQLとするとTouchDBはSQLiteに相当します。
特徴
- モバイル向けのCouchDB互換のNoSQL
- CouchBaseのメンバーが開発
- iOS用(Objective-C実装)とAndroid用(Java実装)がある。
- RestAPIがほとんど一緒(ユーザ管理機能等はなし)
- CouchDBでレプリケートできる(例えばモバイルのTouchDBとサーバのCouchDBを同期できる)
- コードが350KB(ARM7の場合)と軽量
- (デスクトップより)相対的に遅いモバイルCPUでも高速に起動する
- 省メモリ
詳細は公式:couchbaselabs / TouchDB-iOSを参照してください。
CouchCocoa
CouchCocoa.frameworkのビルド
ローカルリポジトリを作成します。
git clone https://github.com/couchbaselabs/CouchCocoa cd CouchCocoa git submodule init git submodule updateXcodeでプロジェクト開いてビルドします。
ビルド結果を確認します。
- XCode > Preferences > Locations を開く
Derived Dataの下のリンクをクリック- CouchCocoa-xxxxxx > Build > Products > Release-ios-universalを開いてCouchCocoa.frameworkができていることを確認
TouchDB
TouchDB.frameworkのビルド
ローカルリポジトリを作成します。
git clone git://github.com/couchbaselabs/TouchDB-iOS.git cd TouchDB-iOS git submodule init git submodule updateXcodeを開いてビルドします。
ビルド結果を確認します。
- XCode > Preferences > Locations を開く
Derived Dataの下のリンクをクリック- TouchDB-xxxxxx > Build > Products > Release-ios-universalを開いて
TouchDB.frameworkができていることを確認
iOSプロジェクトへのTouchDB, CouchCocoaインポート
- Xcodeのプロジェクトを開く
- 先ほどビルドした
CouchCocoa.frameworkをソースツリーのframeworksにドラッグ&ドロップ - 先ほどビルドした
TouchDB.frameworkをソースツリーのframeworksにドラッグ&ドロップ Destinationの"Copy items into~"にチェック- BuildSettingの
Other Linkerに-ObjCを追加 BuiodPhase
Link Binary with Libraliesに以下を追加- CFNetwork.framework
- Security.framework
- SystemConfiguration.framework
- libsqlite3.dylib
- libz.dylib
アプリからTouchDBを利用
didFinishLaunchingWithOptionsなどでDBを起動例:
CouchTouchDBServer* server = [CouchTouchDBServer sharedInstance];
self.database = [server databaseNamed: @"testdb"];
NSError* error;
if (![self.database ensureCreated: &error]) {
NSLog(@"CouchDBが起動しました!");
}
詳細な使い方はまた書くかもしれませんが、公式サイトを参照してください。