web-dev-qa-db-ja.com

iPhone用の静的ライブラリのコアデータ

CoreDataフレームワークを多用する静的ライブラリを構築しました。外部プロジェクトでライブラリを正常に使用できますが、メインプロジェクトに.xcdatamodelファイルを含めた場合に限ります。ライブラリの目的は実装の詳細を可能な限り隠すことだったので、これは理想的とは言えません。

別の 質問 で、リソースをライブラリにバンドルできないと通知されました(これは今では完全に理にかなっています)。

それで、メインプロジェクトにモデルを含めることなく、プログラムでモデルを「発見」できるようにする方法はありますか?

41
Vickram

また、CoreDataを使用する独自の静的ライブラリを作成しました。静的ライブラリに加えて、プロジェクトに別のバンドルターゲットがあり、バンドルリソースのコピーアイテムがあります。これは、いくつかの画像などをバンドルにコピーし、ソースのコンパイルフェーズでxcdatamodelをコンパイルします。

最終的なバンドルには、必要なすべてのファイルが含まれます。静的ライブラリに依存するメインプロジェクトでは、そのバンドルも含める必要があります。これで、メインプロジェクトはコアデータを使用するために必要なmomファイルにアクセスできるようになります。

バンドルのmomでコアデータを使用するには、コードでマージされた管理対象オブジェクトモデルを作成する必要があります(メインプロジェクトにもコアデータモデルがある可能性があります)。


- (NSManagedObjectModel *) mergedManagedObjectModel 
{   
    if (!mergedManagedObjectModel) 
    {
        NSMutableSet *allBundles = [[[NSMutableSet alloc] init] autorelease];
        [allBundles addObjectsFromArray: [NSBundle allBundles]];
        [allBundles addObjectsFromArray: [NSBundle allFrameworks]];

        mergedManagedObjectModel = [[NSManagedObjectModel mergedModelFromBundles: [allBundles allObjects]] retain];
    }

    return mergedManagedObjectModel;
}

バンドルを含めるだけで、xcdatamodelを提供する必要はなく、コンパイルされたmomファイルのみを含める必要があります。

31

Saschaの答えは私を正しい軌道に乗せました。コンパイルされた.momファイルを静的ライブラリから.momファイルにホストプロジェクトからマージするのは比較的簡単でした。簡単な例を次に示します。

  1. MyStaticLibraryという名前の新しいXCode静的ライブラリプロジェクトを作成します

  2. MyStaticLibraryModels.xcdatamodelという名前の.xcdatamodelファイルをMyStaticLibraryに作成し、いくつかのEntitysを追加してから、ヘッダーと実装を生成します。 MyStaticLibraryターゲットをビルドすると、libMyStaticLibrary.aバイナリファイルが生成されますが、コンパイルされた.momファイルは含まれません。そのためには、バンドルを作成する必要があります。

  3. Loadable Bundleの下にあるタイプMacOS X > Cocoaの新しいビルドターゲットを作成し、新しいターゲットMyStaticLibraryModelsを呼び出しましょう。

  4. MyStaticLibraryModels.xcdatamodelMyStaticLibraryModelsターゲットのCompile Sourcesビルドフェーズにドラッグします。 MyStaticLibraryModelsターゲットをビルドすると、MyStaticLibraryModels.bundleというファイルが生成され、コンパイルされたNSManagedObjectModelファイルMyStaticLibraryModels.momが含まれます。

  5. MyStaticLibraryターゲットとMyStaticLibraryModelsターゲットの両方をビルドした後、libMyStaticLibrary.a(および関連するModelヘッダーファイル)とMyStaticLibraryModels.bundleをホストプロジェクトMyAwesomeAppにドラッグします。

  6. MyAwesomeAppCoreDataを使用し、独自の.xcdatamodelファイルを持っており、独自のビルドプロセス中に.momファイルにコンパイルされます。この.momファイルをMyStaticLibraryModels.bundleにインポートしたファイルとマージします。 MyAwesomeAppプロジェクトのどこかに、MyAwesomeAppsNSManagedObjectModelを返すメソッドがあります。このメソッド用に生成されたAppleテンプレートは次のようになります:

.。

- (NSManagedObjectModel *)managedObjectModel {
  if (managedObjectModel_ != nil) {
    return managedObjectModel_;
  }
  NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
  managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];    
  return managedObjectModel_;
}

これを変更して、NSManagedObjectModels、MyAwesomApps、およびMyStaticLibraryModelsの両方を、次のように1つの結合されたNSManagedObjectModelとしてマージして返します。

- (NSManagedObjectModel *)managedObjectModel {
  if (managedObjectModel_ != nil) {
    return managedObjectModel_;
  }

  NSMutableArray *allManagedObjectModels = [[NSMutableArray alloc] init];

  NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
  NSManagedObjectModel *projectManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
  [allManagedObjectModels addObject:projectManagedObjectModel];
  [projectManagedObjectModel release];

  NSString *staticLibraryBundlePath = [[NSBundle mainBundle] pathForResource:@"MyStaticLibraryModels" ofType:@"bundle"];
  NSURL *staticLibraryMOMURL = [[NSBundle bundleWithPath:staticLibraryBundlePath] URLForResource:@"MyStaticLibraryModels" withExtension:@"mom"];
  NSManagedObjectModel *staticLibraryMOM = [[NSManagedObjectModel alloc] initWithContentsOfURL:staticLibraryMOMURL];
  [allManagedObjectModels addObject:staticLibraryMOM];
  [staticLibraryMOM release];

  managedObjectModel_ = [NSManagedObjectModel modelByMergingModels:allManagedObjectModels];
  [allManagedObjectModels release];

  return managedObjectModel_;
}

これにより、NSManagedObjectModelEntityの両方からMyAwesomeAppとマージされたMyStaticLibraryが返されます。

59
prairiedogg

Sascha Konietzkeのソリューションはうまく機能しますが、機能するために提供する必要のある重要な警告が1つあります。モデルを含むバンドルを最初にロードする必要があります。そうしないと、モデルは配列に含まれず、MOMにマージされません。

彼の場合、彼はおそらくすでにバンドルからリソースにアクセスしているため、このコードが実行される前にバンドルがすでにロードされています。

2
Spanner

いいえ、iPhoneアプリでApple以外のフレームワークを使用する際の制限により、OS Xと比較して依存関係ゲームが実際に変わります。ほとんどのiPhoneの「フレームワーク」(Mac用のGoogleのツールボックス、Core Plotなど)は実際には推奨 =製品(つまり静的ライブラリ)をリンクするのではなく、メインアプリケーションプロジェクトにソースを含めること。コミュニティのコンセンサスは、iPhoneでは、フレームワークの利用者がライブラリを使用するために少し「手動」の作業をしなければならないと予想しても問題ないと思います。あなたの場合、これはメインプロジェクトにxcdatamodelファイルを含めています。ほとんどのObjective-Cと同様に、実装の詳細を使用しないようにユーザーに指示し、そのままにしておきます。

2
Barry Wark

Prairiedoggの答えは少し時代遅れです。Xcode5でこれを行うためのチュートリアルは次のとおりです。 http://bharathnagarajrao.wordpress.com/2014/02/14/working-with-core-data-in-a-static -ライブラリ/

2
Liron Yahdav

coreDataを含むライブラリもあります。埋め込みリソースを使用してフレームワークを管理するためのこのテンプレートを見つけました

新しいプロジェクトで使用するのは本当に簡単です(既存のプロジェクトに適用するのはもっと難しいです)が、framewoksビルドの場合は本当にクールです:-)

https://github.com/kstenerud/iOS-Universal-Framework

2
Pixman

Xcdatamodel/momファイルを使用する代わりに、コードでモデルを作成することもできます(特に単純なモデルがある場合)。この方法では、リソースの追加バンドルを作成する必要はありません。これは、2つの属性を含む1つのテーブルを持つ簡単な例です。

- (NSManagedObjectModel *)coreDataModel
{
    NSManagedObjectModel *model = [NSManagedObjectModel new];

    NSEntityDescription *eventEntity = [NSEntityDescription new];
    eventEntity.name = @"EventEntity";
    eventEntity.managedObjectClassName = @"EventEntity";

    NSAttributeDescription *dateAttribute = [NSAttributeDescription new];
    dateAttribute.name = @"date";
    dateAttribute.attributeType = NSDateAttributeType;
    dateAttribute.optional = NO;

    NSAttributeDescription *typeAttribute = [NSAttributeDescription new];
    typeAttribute.name = @"type";
    typeAttribute.attributeType = NSStringAttributeType;
    typeAttribute.optional = NO;

    eventEntity.properties = @[dateAttribute, typeAttribute];
    model.entities = @[eventEntity];

    return model;
}

コードからモデルを作成する方法についてのチュートリアルは次のとおりです。 https://www.cocoanetics.com/2012/04/creating-a-coredata-model-in-code/

また、このアプローチに基づいて、 LSMiniDB と呼ばれる、ニーズに合う可能性のある小さくて使いやすいライブラリを作成したので、それも確認できます。

また、私の場合、NSManagedObjectのプロパティを使用しているときに、コンソールで「警告:動的アクセサーが@property実装を見つけられませんでした...」などの警告がありました。サブクラス。これらのプロパティを別のファイルのカテゴリに含めるのではなく、クラスインターフェイス/実装に移動することで、これを修正できました(現在、xcodeはデフォルトで、このコードを別のファイルClassName + CoreDataClassとClassName + CoreDataPropertiesに分割して生成しています。および各サブクラスのカテゴリ)。

1
Leszek Szary

Saschaの答えのためのSwift2バージョン:

lazy var managedObjectModel: NSManagedObjectModel = {
    // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
    var allBundles = NSMutableSet()
    allBundles.addObjectsFromArray(NSBundle.allBundles())
    allBundles.addObjectsFromArray(NSBundle.allFrameworks())

    let model =  NSManagedObjectModel.mergedModelFromBundles(allBundles.allObjects as? [NSBundle])

    return model!
}()
0
Bill Chan