Причин может быть много, несколько из них, которые и сподвигли меня написать свой мини-маппер, это:
- не хочется разбираться с чем-то большим и сложным;
- мне нужен только маппер, без дополнительных плюшек типа работы с сетью или интеграции с Core Data;
- если фреймворк работает не так как я хочу, часто разобраться и поправить в нем что-то становится реальной головной болью, особенно если стадия проекта далеко не начальная и отказаться от фреймворка проблемно;
- мне не нужен в проекте на 3 экрана фреймворк еще на 50 классов и 4 МБ весом;
- свое всегда роднее.
Первым делом ссылка на GitHub, где лежит исходник базового класса для наших будующих моделей и пример проекта с использованием маппера.
Прежде чем хвалить или еще что-то, опишу минусы:
- на каждый json объект, какой бы крошечный он не был, придется создать Objective-c класс (хотя может это плюс?);
- нет никаких проверок входных данных;
- внимательность разработчика, некоторые вещи не проверяются (нужно мапить массив кастомных объектов, но не указано в какие классы будет оно мапиться, например);
- еще что-то, доказывающее что маппер абсолютно неправильный и использовать его ни в коем случае нельзя, о чем непременно обоснованно напишут в комментариях.
Из плюсов — простой в использовании, крошечный как по размеру так и по количеству классов, в которых в случае чего придется разбираться, делает свое дело.
Как с ним работать? Маппер состоит из одного класса — TinyMappingModel, это базовый класс для всех последующих классов модели. На каждый объект json создается по наследнику TinyMappingModel, который должен содержать в себе свойства для хранения нужных данных. В идеальном случае стоит называть их так же, как называются соответствующие поля в json — тогда маппинг будет происходить сам по себе, как по волшебству (KVC), о случаях, когда это невозможно (например, поле с название im:name, id, 1work), напишу ниже.
TinyMappingModel содержит в себе 4 метода:
//public + (instancetype)mapObjectFromDictionary:(NSDictionary *)data; + (NSArray *)mapArrayOfObjects:(NSArray *)data; //protected methods - (NSDictionary *)keyToClassMappingRules; - (NSDictionary *)keyToPropertyNameReplacementRules;
Hаследники по необходимости должны переопределять два последних.
— (NSDictionary *)keyToClassMappingRules; — переопределяется в случае, если нам нужно замапить json объект в какой-то кастомный класс (наследник TinyMappingModel) или в массив. Метод должен возвращать словарь с парами ключ — имя поля в json, значение — класс в который будет мапиться объект или, в случае коллекции, из каких объектов будет состоять коллекция. Например:
- (NSDictionary *)keyToClassMappingRules { return @{@"im:name":[TitleModel class], @"im:image":[ImageModel class]}; }
— (NSDictionary *)keyToPropertyNameReplacementRules; — переопределяется в случае, если мы не можем/хотим по каким-то причинам назвать свойство в классе так же, как оно называется в json. Ключ — имя поля в json, значение — название свойства в классе, например:
- (NSDictionary *)keyToPropertyNameReplacementRules { return @{@"im:name":@"name",@"im:image":@"images"}; }
Два данных метода из примера будут единственным, что минимально необходимо реализовать в имплементации. В хедере нашего класса (например, EntryModel) будет:
@class TitleModel; @interface EntryModel : TinyMappingModel @property (nonatomic, strong) TitleModel *name; @property (nonatomic, strong) NSArray *images; @end
С name все понятно, в массиве images будут храниться объекты типа ImageModel, и, само собой, в имплементации нужно импортить нужные классы (в данном случае ImageModel и TitleModel), можно было бы, конечно, сделать на строках, а потом NSClassFromString, но нет.
Далее, как же работать-то? Когда мы получили из сети данные и каким-то образом преобразовали их в json (например, я часто в небольших проектах использую AFNetworking), следует:
//data - NSDictionary c json EntryModel *model= [EntryModel mapObjectFromDictionary:data]; //или data - NSArray EntryModel *modelArray= [EntryModel mapArrayOfObjects:data];
Возможны вариации. Главное передать правильный объект в правильный метод, оно там разберется, ничего сложного.
В двух словах опишу как работает маппер.
Самый важный метод + (instancetype)mapObjectFromDictionary:(NSDictionary *)data, он по сути и делает всю работу — создает будующую модель, итерируется по ключам json, решает во что мапить будем (класс – в зависимости от того, что у нас в keyToClassMappingRules и в какое именное свойство – в зависимости от keyToPropertyNameReplacementRules). Далее, в зависимости от того, что из себя представляют данные для текущего ключа и того, во что будем мапить, есть три пути развития событий:
- данные массив — будет вызван mapArrayOfObjects классу, в который мапим, и результат установится в соответствующее свойство;
- данные объект — будет вызван mapObjectFromDictionary (этот же метод, но другого класса, класса из -keyToClassMappingRules), в который мапим, и результат установится в соответствующее свойство;
- данные «примитив» (NSString, NSNumber etc) и keyToPropertyNameReplacementRules ничего не вернул для данного ключа — KVC установит в нужное свойство без вопросов.
Вот в общем и все. Если подытожить, у нас есть три «сущности» — что мапить, в какой класс мапить или в «примитив», в какое свойство мапить (или если не указано, то в свойство с именем ключа json), и в зависимости от того массив ли значение, которое мапиться или нет, вызывается mapObjectFromDictionary или mapArrayOfObjects, соответсвенно. mapArrayOfObjects никакой работы по сути не делает, создает массив и кладет в него [self mapArrayOfObjects:value] или [mappedArray addObject:[self mapObjectFromDictionary:value], где value — значение, получаемое в ходе итерации по входному массиву. Вот и все.
Спасибо за внимание, интересно ваше мнение об удобстве использования, и, если вы посмотрели исходник, о маппере.
ссылка на оригинал статьи http://habrahabr.ru/post/181740/
Добавить комментарий