mogenerator для Core Data, о котором нужно знать

в 7:47, , рубрики: core data, mogenerator, xcode, Программирование, разработка под iOS, метки: , ,

mogenerator для Core Data, о котором нужно знать

Относительно недавно я узнал о mogenerator — замечательном приложении командной строки для генерации классов на основании объектного графа Core Data. И вот что, если вы о нем ещё не знаете, то обязательно стоит с ним познакомиться, использование этого приложения значительно упрощает изменение объектного графа Core Data. mogenerator генерирует по два класса на сущность, один для машинного использования, другой для обеспечения возможности добавления дополнительного функционала. Корректно настроив проект в XCode возможно производить эту генерацию автоматически перед сборкой.
В этой статье я опишу как можно использовать mogenerator с XCode 4 и некоторые полезные мелочи.

Как уже было описано выше, mogenerator генерирует по два класса на сущность, всего четыре файла. Пусть сущность называется Entity, для такой сущности будет сгенерированы следующие файлы:

  • _Entity.h, _Entity.m — содержат класс _Entity, эти два файла будут перегенерированы при каждом изменении сущности Entity, после добавления их в проект о них можно забыть,
  • Entity.h, Entity.m — содержат класс Entity, наследованный от _Entity, эти два файла будут сгенерированы только один раз, их можно модифицировать, они не будут перезаписаны.

mogenerator для Core Data, о котором нужно знать
Класс Entity будет использован в коде для представления сущности Entity и что самое замечательное, класс _Entity будет синхронизирован с моделью данных! Один раз настроив mogenerator и добавив сгенерированные файлы в проект можно забыть об актуализации файлов, об этом позаботятся автоматически.

Пока я не знал о существовании mogenerator я использовал категории, у меня для сущности Entity существовали следующие файлы:

  • Entity.h, Entity.m — содержат класс Entity, эти два файла я перегенерировал вручную после изменения сущности в модели,
  • EntityEx.h, EntityEx.m — содержат категорию Ex для класса Entity, эти два файла я использовал для добавления функционала, в других файлах подключал EntityEx.h вместо Entity.h.

mogenerator для Core Data, о котором нужно знать
Конечно во многих случаях такой подход вполне работает, но я не переопределял существующих методов и почти не использовал наследование сущностей. Кроме того в результате любых изменений объектного графа мне приходилось генерировать классы для сущностей вручную.

Настройка автоматической генерации файлов с помощью mogenerator в XCode 4 (на примере XCode 4.6.2)

0. Конечно нужно скачать и установить mogenerator.
1. Создать скрипты, которые будут запускать mogenerator с необходимыми параметрами, скрипты что использую я можно получить тут. Пример скрипта:

# Описанные каталоги должны существовать
# Каталог для файлов, которые будут сгенерированы один раз и которые можно редактировать
HUMAN_DIR="${PROJECT_DIR}/MogeneratorDemo/CoreData/Entities"
# Каталог для фалов, которые будут постоянно перезаписываться и которые трогать не нужно.
MACHINE_DIR="${PROJECT_DIR}/MogeneratorDemo/CoreData/EntitiesMachine"
# Полный путь к файлу, в который будут помещены подключения (#import "{className}.h") всех сгенерированных .h из HUMAN_DIR
INCLUDE_H="${PROJECT_DIR}/MogeneratorDemo/CoreData/ModelIncludes.h"

mogenerator --model "${INPUT_FILE_PATH}" --machine-dir "$MACHINE_DIR/" --human-dir "$HUMAN_DIR/" --includeh "$INCLUDE_H" --template-var arc=true

Обратите внимание на последний параметр: --template-var arc=true, благодаря ему сгенерированные файлы будут совместимы с ARC.
Чтобы определить базовый класс отличный от NSManagedObject используется параметр --base-class.

2. Теперь в XCode, выбираем нужный target, открываем таб «Build Rules», добавляем два правила, одно для «Data model version files»:

mogenerator для Core Data, о котором нужно знать

, другое для «Data model files»:

mogenerator для Core Data, о котором нужно знать

Не забудьте указать строчку в Output Files, без неё скрипты вообще не будут запущены (поправьте меня если я не прав).

3. Для каждой сущности в модели необходимо указать корректный класс (обычно совпадает с именем сущности):

mogenerator для Core Data, о котором нужно знать

Иначе mogenerator пропустит сущности и не будет генерировать для них файлы.

4. Всё, теперь можно попробовать построить проект (Build, command B)
Чтобы узнать результат работы mogenerator можно заглянуть в Log Navigator и выбрать последний билд:

mogenerator для Core Data, о котором нужно знать

На изображении одна из проблем, которая может встретится:

"Print: Entry, "_XCCurrentVersionName", Does Not Exist"

Такое получается, если в модель данных с поддержкой версий ещё не добавлялось ни одной версии, например, вы только что её создали. Точно не уверен большая ли это проблема, но она исправляется добавлением и удалением (%Model%.xcdatamodeld «Show package contents», вручную удаляем .xcdatamodel или через терминал, как удобно) версии модели данных. После такого удаления необходимо удалить и добавить .xcdatamodeld файл в проект.

Если всё сделано правильно в Log Navigator можно увидеть примерно следующее:

mogenerator для Core Data, о котором нужно знать

5. Сгенерированные файлы сами в проект не добавятся, необходимо сделать это самостоятельно:

mogenerator для Core Data, о котором нужно знать

Достаточно подключить файл ModelIncludes.h и в нужном месте будут доступны все сгенерированные .h. Один раз добавив новосгенерированный файл в проект о нем можно забыть, перед каждой сборкой необходимые файлы будут перегенерированы.

Я собрал демонстрационный проект, в котором mogenerator настроен описываемым образом.

Некоторые полезные мелочи

Transformable атрибуты

Чтобы хранить нестандартные типы данных в Core Data предусмотрены два типа атрибутов: Binary Data и Transformable. Больший интерес представляет второй тип, т.к. он позволяет избежать необходимости ручной сериализации объектов в NSData. В модели данных из проекта у сущности CDPlace выделен атрибут coordinates, он предназначен для хранения координат в виде экземпляра Coords2D. Coords2D — легкий класс для работы с координатами, поддерживающий NSCoding. Обратите внимание на его тип — Transformable, благодаря такой конфигурации возможно, например, следующее:

    CDPlace* cdPlace = …    
    Coords2D* coords2D = ...
    cdPlace.coordinates = coords2D; // сериализация происходит автоматически

Проблема появляется при использовании Transformable атрибутов с mogenerator — как указать класс, чтобы property был сгенерирован корректно?

    @interface _CDPlace : NSManagedObject {}
    ...
    @property (nonatomic, strong) Coords2D* coordinates; // откуда mogenerator узнает что нужно подставить Coords2D?

Решение следующее: необходимо добавить в UserInfo атрибута пару attributeValueClassName => %имя_класса%:
1. В Xcode выбираем атрибут coordinates,

mogenerator для Core Data, о котором нужно знать

2. Справа выбираем вкладку Data Model Inspector,
3. В группе User Info жмем + под списком и добавляем:

mogenerator для Core Data, о котором нужно знать

При следующей генерации property coordinates будет типа Coords2D.

Переименование сущности

Если переименовать сущность, mogenerator просто сгенерирует новую пару классов, удаление лишних и переименование необходимых файлов необходимо производить вручную.
Применение совместно параметров --orphaned и --model позволяет получить список исходных файлов, для которых в модели не найдено соответствующих сущностей. Например можно перенаправить вывод mogenerator в xargs + git чтобы удалить файлы для удаленных и переименованных сущностей в одну комманду:

$ mogenerator --model ../Model.xcdatamodel --orphaned | xargs git rm

Что ещё?

mogenerator поддерживает пользовательские шаблоны, т.е. существует возможность изменить способ наполнения файлов как вздумается, унифицировать свой boilerplate код, создать свой шаблон и использовать его с mogenerator.
Стандартный шаблон предоставляет следующий набор плюшек:

  • Валидация — Удобно переопределять методы валидации атрибутов.
  • %attribute%Value — для числовых и bool типов есть удобные сеттеры, которые позволяют избежать возню с NSNumber, например:
    NSInteger sum = entity.int32AtributeValue + 5; // vs [entity.int32Atribute integerValue] + 5
    entity.int32AtributeValue = 500; // vs entity.int32Atribute = @500 or [NSNumber numberWithInteger: 500]
    

  • Удобные методы для работы с MOC:
    Entity* entity = [Entity insertInManagedObjectContext:context]; // добавляем новый Entity в MOC context
    NSString* entityName = [Entity entityName]; // получаем имя сущности
    NSEntityDescription* entityDescription = [Entity entityInManagedObjectContext:context]; // получаем EntityDescription в MOC context
    

  • Метод для фетчинга всех экземпляров сущности в хранилище:
    NSArray* allEntities = [Entity fetchAllEntities:context];
    

Это не полный набор, стандартный шаблон для генерации классов развивается вместе с mogenerator.

Итого имеем отличный инструмент игнорировать который на мой взгляд сегодня уже нельзя.

Автор: MANIAK_dobrii

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js