Основные данные в статической библиотеке для iPhone
Я создал статическую библиотеку, которая сильно использует структуру Core Data. Я могу успешно использовать библиотеку в моем внешнем проекте, но ТОЛЬКО, если я включу файл .xcdatamodel в основной проект. Это меньше, чем идеально, поскольку точка библиотеки заключалась в том, чтобы максимально скрыть детали реализации.
В отдельном question мне сообщили, что я не могу связать ресурсы с библиотекой (что теперь имеет для меня полный смысл).
Итак, есть ли способ программно разрешить "обнаружение" модели без необходимости включать модель в основной проект?
Ответы
Ответ 1
Я также создал свою собственную статическую библиотеку, которая использует Core Data. Помимо статической библиотеки у меня есть еще одна цель пакета в проекте, где у меня есть элемент "Копирование запаса", который копирует некоторые изображения и тому подобное в пакет и фазу сборки источников компиляции, где я компилирую xcdatamodel.
Последний комплект будет содержать все необходимые файлы. В вашем основном проекте, который опирается на статическую библиотеку, вы должны также включить этот пакет. Теперь у вашего основного проекта будет доступ к файлу мамы, который необходим для использования основных данных.
Чтобы использовать основные данные с мамой из пакета, вы должны создать объединенную модель управляемых объектов в своем коде (может быть, в основном проекте есть и основная модель данных):
- (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, должен быть включен только файл скомпилированной мамы.
Ответ 2
Саша ответил мне на правильный путь. Слияние скомпилированного файла .mom
из статической библиотеки в файл .mom
из хост-проекта было относительно простым. Здесь тривиальный пример:
-
Создайте новую статическую библиотеку XCode
проект под названием MyStaticLibrary
-
Создайте файл .xcdatamodel в MyStaticLibrary
под названием MyStaticLibraryModels.xcdatamodel
, добавьте несколько Entity
s, затем сгенерируйте заголовки и реализации. Когда вы создаете цель MyStaticLibrary
, вы создадите двоичный файл libMyStaticLibrary.a
, но он не будет содержать скомпилированный файл .mom
. Для этого нам нужно создать пакет.
-
Создайте новый объект сборки типа Loadable Bundle
, найденный под MacOS X > Cocoa
, позвоните в новый Target MyStaticLibraryModels
.
-
Перетащите MyStaticLibraryModels.xcdatamodel
в фазу сборки Compile Sources
объекта MyStaticLibraryModels
. Когда вы построите MyStaticLibraryModels
Target, вы сгенерируете файл с именем MyStaticLibraryModels.bundle
, и он будет содержать скомпилированный файл NSManagedObjectModel
, MyStaticLibraryModels.mom
.
-
После создания целей MyStaticLibrary
и MyStaticLibraryModels
перетащите libMyStaticLibrary.a
(вместе с любыми связанными файлами заголовка модели) и MyStaticLibraryModels.bundle
в проект вашего хоста MyAwesomeApp
.
-
MyAwesomeApp
использует CoreData
, имеет собственный файл .xcdatamodel
, который будет скомпилирован в файл .mom во время собственного процесса сборки. Мы хотим объединить этот файл .mom
с тем, который мы импортировали в MyStaticLibraryModels.bundle
. Где-то в проекте MyAwesomeApp
существует метод, который возвращает MyAwesomeApp
NSManagedObjectModel
. Шаблон, созданный Apple для этого метода, выглядит следующим образом:
...
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return managedObjectModel_;
}
Мы изменим это, чтобы объединить и вернуть ОБА наших NSManagedObjectModel
s, MyAwesomApp
и MyStaticLibraryModels
, как единый, объединенный 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_;
}
Это вернет объединенный NSManagedObjectModel
с Entity
из MyAwesomeApp
и MyStaticLibrary
.
Ответ 3
Нет, ограничение использования фреймворков, отличных от Apple, в iPhone-приложении действительно изменяет игру зависимостей относительно OS X. Большинство "фреймворков" iPhone (например, панель инструментов Google для Mac, Core Plot и т.д.) на самом деле рекомендуют включить источник в вашем основном проекте приложения, а не связывание продукта (т.е. статическая библиотека). Я думаю, что консенсус сообщества заключается в том, что на iPhone все в порядке ожидать, что потребители вашей инфраструктуры должны будут немного поработать над вашей библиотекой. В вашем случае это файл xcdatamodel в основном проекте. Как и в большинстве Objective-C, сообщите своим пользователям, чтобы они не использовали детали реализации и не оставляли их на этом.
Ответ 4
У меня также есть библиотека с coredata.
Я нашел этот шаблон для управления каркасом с помощью встроенных ресурсов
он действительно прост в использовании в новом проекте (более сложный для применения на существующих)
но для создания фреймовок, это действительно здорово: -)
https://github.com/kstenerud/iOS-Universal-Framework
Ответ 5
Решение Sascha Konietzke работает хорошо, но есть одно важное предостережение, которое необходимо предоставить для его работы. Сначала необходимо загрузить пакет, содержащий модель, иначе он не будет включен в массив и объединен в MOM.
В его случае он, вероятно, уже получил доступ к ресурсам из пакета, поэтому пакет был уже загружен до выполнения этого кода.
Ответ 6
Ответ на предикацию немного устарел, вот учебник по этому поводу в Xcode 5: http://bharathnagarajrao.wordpress.com/2014/02/14/working-with-core-data-in-a-static-library/
Ответ 7
Версия Swift 2 для ответа Sascha:
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!
}()
Ответ 8
Обратите внимание, что вместо использования файла xcdatamodel/mom вы также можете создать свою модель в коде (особенно если у вас простая модель), и вам не понадобится создавать дополнительный пакет для ресурсов. Вот простой пример с одной таблицей, которая содержит два атрибута:
- (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, чтобы вы могли проверить это также.