Pull to refresh

Использование MagicalRecord при разработке iOS приложений

Reading time 20 min
Views 23K
Original author: Andy Pereira
Хабралюди, добрый день!

Сегодня Вашему вниманию хочу представить очередной перевод, не судите строго :) Надеюсь, Вам этот материал пригодится в работе.

В течение многих лет, Core Data была неотъемлемой частью многих OS X и iOS-приложений, обеспечивая сохранение и запрашивая пользовательские данные. Компания Apple тратит много усилий для того, чтобы API Core Data было проще в использовании и облегчала разработчикам процесс интегрирования в приложения.

Этот факт указывает на то, что Core Data является сложно-модифицируемой проектом. Даже если вы знаете как пользоваться Data Core, выполнение простых, ежедневных задач может показаться вам сложной и объемной работой. Хорошо, что существует MagicalRecord – независимая библиотека для Core Data, созданная MagicalPanda. А это учебное пособие научит Вас, как ускорить работу с MagicalRecord быстро и легко.

MagicalRecord – прост в использовании, хорошо разработан и популярен. Авторы проекта заявил, что основная задача MagicalRecord – очистка кода который Вам нужно писать для использования Core Data и с помощью одной простой строки кода сделать выборку данных, одновременно позволяя пользователю оптимизировать производительность. «Как это возможно?» — подумаете Вы. Это возможно благодаря удобной технологии, которая использует один и тот же шаблон для настройки, запросов и обновления Core Data. Особенностью дизайна является влияние Ruby on Rails’ системы хранения данных ActiveRecord.



Достаточно теории! Следуйте инструкциям, и вы увидите, как работает MagicalRecord. В этой статье рассказывается о том, как создать приложение которое отследит какое же пиво Ваше любимое. Это позволит Вам:

Добавить пиво по вкусу.

  • Оценить пиво.
  • Сделать заметки о пиве.
  • Сделать фотографию пива (на тот, случай, если вы перебрали, а заметку сделать нужно)


Начнем

Для полного понимание этой статьй, Вы уже должны владеть базовой пониманием CoreData, на уровне учебного пособия по CoreData. Кроме этого, Вам не требуется какого-либо предварительного опыта работы с CoreData.

Если у вас отсутствует даже базовый опыт работы с Core Data, я рекомендую все же сначала ознакомиться с этим учебным руководством Core Data, а затем вернуться сюда.

Для начала, загрузите этот стартовый проект. Как только он загрузится, распакуйте архив и запустите приложение, что бы проверить его.

image


Приложение имеет базовый навигационный контроллер с кнопкой «Добавить», так же таблицу, поисковую строку (если вы потяните вниз пустую таблицу) и cегментный выбор для сортировки по алфавиту или по рейтингу. Если Вы нажимаете кнопку «Добавить», вы перейдете к интерфейсу, где Вам нужно будет ввести название пива и просмотреть информацию о нем. Если вы попытаетесь ввести что-нибудь, то пока что оно не будет сохраняться.

Теперь осмотрите код. В Project Navigator Вы увидите:

  • Все контроллеры, которые вы используете для работы приложения.
  • Служебный класс ImageSaver
  • Изображения, которые вам пригодятся в дальнейшем для заполнения исходных данных
  • Библиотека Images.xcassets, в которой уже есть несколько изображений, использующихся UI
  • AMRating — удобный для рейтинг-контроля в других библиотеках


Просматривая, вы можете заметить, что отсутствует CoreData модель, а так и в AppDelegate.m не содержится кода для установки. Это идеальный сценарий для любого проекта, который Вы начали без Core Data. Но понимание, что без Core Data – никуда, придет только потом.

Знакомство с MagicalRecord

В Project Navigator выбирите и раскройте группу MagicalRecord. Внутри Вы увидите группы Categories и Core, а также CoreData+MagicalRecord.h. Раскройте группу Categories и откройте файл под названием NSManagedObjectModel+MagicalRecord.h.

Вы заметите, что, все названия начинаются с префикса MR_. На самом деле, если Вы заходите через какие-либо файлы в группу Categories, Вы обратите внимание на то, что все названия, начинающиеся именно с префикса MR_.

Я не знаю как вам, но мне кажется довольно странным начинать каждое название одними и теми же буквами. Хорошо что MagicalRecord позволяет с легкостью изменять их используя Shorthand Support.

Опять же, в Project Navigator, раскройте группу Supporting Files, и откройте файл BeerTracker-Prefix.pch. Это предкомпилированный заголовок для проекта. Добавьте две строки кода в «стартовый проект» в этот файл:

#define MR_SHORTHAND
#import “CoreData+MagicalRecord.h”


Эти две строки активируют MagicalRecord для вашего проекта:

  1. MR_SHORTHAND рассказывает MagicalRecord, что Вы не хотите вводить MR_ перед каждым методом с MagicalRecord. Зайдя в MagicalRecord+ShorthandSupport.m Вы можете узнать больше о том, как это работает. Но это выходит за рамки этой статьй, и потому здесь обсуждаться не будет :)
  2. Путем импорта CoreData+MagicalRecord.h, теперь Вы можете получить доступ к любому из MagicalRecord API в проектных файлах, и Вам больше не придется импортировать их снова и снова в каждый файл.


Примечание: если Вы хотите добавить MagicalRecord в свой проект, Вы можете воспользоваться дополнительными советами на их странице GitHub.

Также Вы можете добавить MagicalRecord тем же образом, как и я добавил его в проект:

  1. Скачать проект MagicalRecord с GitHub
  2. Перетащить MagicalRecord (содержащий CoreData+MagicalRecord.h, Categories, and Core) приямиком в ваш Xcode проект
  3. Убедитесь, что CoreData база добавлена в настройки проекта в Build Phases\Link Binary with Libraries
  4. В Вашем предкомпилированном заголовке, добавьте следующее #ifdef __OBJC__:

#define MR_SHORTHAND
#import “CoreData+MagicalRecord.h”


Модель «Пива»

Чтобы начать отслеживать Ваши любимые сорта, Вам потребуется создать для этого модель, потому как Вы наврядли сможете всех их запомнить, так ведь? В в меню Xcode выберите File\New\File… из списка слева, выбирите Core Data и выбирайте — Data Model.

image

Назовите файл BeerModel.xcdatamodeld и переместите его в группу BeerTracker.

В project navigator выбирите новый комплект BeerModel.xcdatamodeld и начните его редактирование. Добавьте объект и назовите его «Beer». Добавить свойсво — name типа String.

image

Добавите другой объект, с названием BeerDetails. Этот объект будет следить за такими показателями как: пользовательский рейтинг, заметки и где найти изображение. Добавьте следующие свойства BeerDetails, соответствующих типов:

  • Свойство: Изображение. тип: String
  • Свойство: Примечание. тип: String
  • Свойство: Рейтинг. тип: Integer 16


image

Далее создайте связь между двумя обьектами, поэтому что объект пиво должен знать, что BeerDetails принадлежит ему. В BeerDetails, создайте новые отношения и назовите его “пиво” с целевой пива. Выбрав назначения пива, Вы создали первую часть отношений между субъектами.

image

Завершить настройку взаимосвязи путем выбора объекта Beer. Добавите взаимосвязь с наименованием beerDetails, с назначением BeerDetails и инвертируйте Beer. Теперь ваш объект Beer будет иметь объект BeerDetails, чтобы отличать такие же сорта.

image

Следующий шаг — это классифицировать данные для представления объектов. Для этого используйте Xcode, но Вы должны быть осторожны, потому что Xcode иногда глючит.

Во-первых, измените модель CoreData выбрав ее в Xcode project navigator; убедитесь, что Вы выделили объект Beer, в панели “объекты” а не среди объектов BeerDetails.

Далее, в меню Xcode зайдите в Editor\Create NSManagedObject Subclass… проверьте BeerModel, затем нажмите Next. Под субъектами управления, установите флажки для Beer и BeerDetails, если они не выставлены по умолчанию, и проверьте дважды, что Beer выделяется. Нажмите «Next» и потом «Create». Таким образом Вы создадите новые классы, Beer и BeerDetails с соответствующими объектами и именами. Посмотрите на новые классы, и Вы увидите, что каждый класс имеет свойства, соответствующие свойства объектов, которые Вы определили.

Проверьте еще раз свойства, которые представляют взаимосвязь между сущностями. Вы должны увидеть, что класс Beer содержит свойство beerDetails типа BeerDetails*object. Но, вы также увидите, что класс BeerDetails содержит свойство пива сорта NSManagedObject*. Почему свойства не имеют Тип пива*? Это из-за ограничений в команде «Xcode Create NSManagedObject Subclass». Он не влияет на этот проект, так что просто игнорируйте ее.

Примечание: Пытали любопытство? Интересно, почему Xcode не создает cdjqcndf*Beer? По-видимому, это — результат ограничений в команде Xcode-а CreateNSManagedObjectSubclass. С тех пор как Xcode распознает CoreData Model, логично предположить, что команда должна быть в состоянии сделать вывод, о том, что свойства каждого класса должны иметь взамоствязь друг с другом.

Однако, эта команда не только генерирует свойства разных типов классов. Она также генерирует имена классов, используемых для определения этих типов, и команда не достаточно правильна, чтобы полностью выполнить обе задачи. Во-первых, можно просто сгенерированный код и установить точное свойство типа и подкласса (добавляя по мере необходимости описание). Другой способ — это уменьшить количество выполняемых задач для Xcode. Если вы четко определили имя класса для каждого объекта в Data Model Inspector, прежде чем создать сами классы, тогда Xcode создаст правильные свойства типов.

Если Вам интересны, более сложные инструменты для создания классов из Xcode data models, один из них — mogenerator.

Теперь, когда Вы создали классы для объектов данных, пришло время инициализации стэка Core Data. Откройте файл AppDelegate.m, и в метод делегата application:didFinishLaunchingWithOptions: добавьте следующие строки кода перед оператором return:

// Setup CoreData with MagicalRecord
// Step 1. Setup Core Data Stack with Magical Record
// Step 2. Relax. Why not have a beer? Surely all this talk of beer is making you thirsty…
[MagicalRecord setupCoreDataStackWithStoreNamed:@"BeerModel"];


Если Вам доводилось работать с проектом Xcode, созданным Core Data, Вы вероятно, видели, сколько кодов требуется для подключения Core Data, и ее инициализации в файле AppDelegate. С MagicalRecord, все, что вам нужно — всего одна строчка кода!

MagicalRecord предоставляет несколько альтернативных способов настройки стека Core Data, в зависимости от следующего:

  • Тип резервного хранилища
  • Хотите ли Вы использовать auto-migration
  • И имя вашего файла Core Data Model


Если в Ваш файл Model имеет то же базовое имя, что и имя проекта (например, файл модели BeerTracker.xcdatamodeld, в рамках проекта, названного BeerTracker), то Вы MagicalRecord можете воспользоваться одной из трех удобных способов -setupCoreDataStack, setupCoreDataStackWithInMemoryStore, или setupAutoMigratingCoreDataStack.

Но, поскольку ваши модели называется по-другому, то вы должны использовать один из двух других видов установки: setupCoreDataStackWithStoreNamed: или setupCoreDataStackWithAutoMigratingsqlitestorenamed:

Используя метод настройки AutoMigrating, Вы можете изменить свою модель, и если есть возможность автоматического переноса вашего хранилища, MagicalRecord будет делать это за Вас. Как правило, Core Data требует от Вас добавления кода, чтобы обработать небольшие изменения, внесенные в модель.

Пивоварение (entity)

Теперь, ваша модель и стек Core Data — настроины. Вы можете начать добавлять пиво в свой список. Откройте файл BeerViewController.h, и после class AMRatingControl, добавьте:

@class Beer;

А также можно добавить свойства пива после interface:

@property (nonatomic, strong) Beer *beer;


Потом перейдите к файлу BeerViewController.m и импортируйте Beer.h и BeerDetails.h:

#import "Beer.h"
#import "BeerDetails.h"


В viewDidLoad, добавьте следующий блок кода:

- (void)viewDidLoad {
    // 1. If there is no beer, create new Beer
    if (!self.beer) {
        self.beer = [Beer createEntity];
    }
    // 2. If there are no beer details, create new BeerDetails
    if (!self.beer.beerDetails) {
        self.beer.beerDetails = [BeerDetails createEntity];
    }
    // View setup
    // 3. Set the title, name, note field and rating of the beer
    self.title = self.beer.name ? self.beer.name : @"New Beer";
    self.beerNameField.text = self.beer.name;
    self.beerNotesView.text = self.beer.beerDetails.note;
    self.ratingControl.rating = [self.beer.beerDetails.rating integerValue];
    [self.cellOne addSubview:self.ratingControl];
 
    // 4. If there is an image path in the details, show it.
    if ([self.beer.beerDetails.image length] > 0) {
        // Image setup
        NSData *imgData = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:self.beer.beerDetails.image]];
         [self setImageForBeer:[UIImage imageWithData:imgData]];
     }
}


Устранение неполадок: если у Вас возникли ошибки после копирования предыдущего блока кода в свой проект, и сделайте Clean для Вашего проекта, нажмите Shift+Command+K, или перейдите в Product\Clean.

BeerViewController загружается потому что:
  • Или выбрано пиво, или ….
  • Нажали кнопку добавить из MasterViewController


Теперь, когда view загрузился Вам нужно будет сделать следующее:

  1. Проверьте загружен ли объект Beer. Если нет, то это означает, что Вам нужнно добавить новый сорт пива.
  2. Если Beer не имеет каких-либо BeerDetails, создайте BeerDetails объекта.
  3. Для настройки просмотра, введите название пива, его рейтинг и обратите внимание на содержание. Если у этого пива еще нет названия (в случае нового ввода нового пива, проект даст ему название “новое пиво”).
  4. Если пиво содержит путь к изображению, оно будет загружать изображения в UIImageView.


Есть несколько вещей, которые должны установить таким образом, чтобы Вы могли редактировать и добавлять новые сорта пива. Во-первых, вы должны быть в состоянии добавить или изменить имя. Редактировать textFieldDidEndEditing: так, чтобы он выглядел, как показано ниже:

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ([textField.text length] > 0) {
        self.title = textField.text;
        self.beer.name = textField.text;
    }
}


Теперь, когда Вы завершите добавление именем textField, он установит название пива и все содержимое textField, пока поле не заполнится.

Чтобы сохранить содержимое заметки, пиво нужно оценить, найти textViewDidEndEditing:, и измените содержимое мотода, чтобы оно выглядело, как показано ниже:

- (void)textViewDidEndEditing:(UITextView *)textView {
    [textView resignFirstResponder];
    if ([textView.text length] > 0) {
        self.beer.beerDetails.note = textView.text;
    }
}


Далее, убедитесь, что рейтинг пива обновлен, после того как пользователь изменяет его в меню: View Controller. Найти метод updateRating, и добавьте следующее:

- (void)updateRating {
    self.beer.beerDetails.rating = @(self.ratingControl.rating);
}


Когда пользователь нажимает на UIImageView на странице «детали», это позволяет ему добавить или изменить фото. В UIActionSheet displays, который позволяет пользователю выбрать изображение сделанное своей камерой, или сделать новый снимок. Если пользователь хочет, сфотографировать, он должен убедиться, что изображение, сохраняется и на диске. Вместо сохранения изображения в CoreData (что может вызвать проблемы с производительностью компьютера) сохраните изображение в документах пользователя, со остальными фотографиями, а этот путь используется только для Core Data.

Управления взаимодействием между камерой и библиотекой фотоснимков приводится путем внедрения методов протокола UIImagePickerControllerDelegate. Вам нужно сделать BeerViewController делегатом для UIImagePickerController, поскольку это контроллер обработки storyboard. Найдите метод imagePickerController:didFinishPickingMediaWithinfo:, и добавьте следующее:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    // 1. Grab image and save to disk
    UIImage *image = info[UIImagePickerControllerOriginalImage];	
    // 2. Remove old image if present
    if (self.beer.beerDetails.image) {
        [ImageSaver deleteImageAtPath:self.beer.beerDetails.image];
    }
    // 3. Save the image
    if ([ImageSaver saveImageToDisk:image andToBeer:self.beer]) {
        [self setImageForBeer:image];
    }
    [picker dismissViewControllerAnimated:YES completion:nil];
}


Вот что происходит в коде, приведенном выше:
  1. imagePickerController:didFinishPickingMediaWithInfo: косвенно передает ссылку на изображение выбранное пользователем, где само изображение уже находится в info-словаре, под UIImagePickerControllerOriginalImage ключом ...
  2. Если объект Beer уже содержит изображение, приложение удалит его с диска, так чтобы хранилище Пользователя без необходимости не было переполненным.
  3. А затем новое изобржение сохраняется на диск и путь его добавляется к BeerDetails в свойства изображения. Откройте ImageSaver и найдите saveImageToDisk:andToBeer чтобы увидеть, как это выглядит. После того, как изображение успешно сохранено, оно отображается в UIImageView.
  4. После чего picker скрывается.


Вам потребуется немного изменить ImageSaver, чтобы приступить к работе. Теперь, когда класс Beer создан, Вы можете не комментировать строку импорта, и строку, которая задает путь к объекту изображения. Откройте файл ImageSaver.m и изменить инструкции импорта:

#import "ImageSaver.h"
#import "Beer.h"
#import "BeerDetails.h"


Теперь Вам нужно раскомментировать строку с if инструкцией.

if ([imgData writeToFile:jpgPath atomically:YES]) {
        beer.beerDetails.image = path;
}


В класс ImageSaver теперь полностью готовы к тому, чтоб принять изображение и сохраните его на телефоне, документов, каталогов, и пути в BeerDetails объекту.



Исходя из этого у пользователя есть два варианта: отменить или добавить новое пиво. При создании view, объект Beer был создан, и вставлен в managedObjectContext. Отмена должна удалить объект Beer. Найдите метод cancelAdd, и добавьте следующее:


- (void)cancelAdd {
    [self.beer deleteEntity];
    [self.navigationController popViewControllerAnimated:YES];
}


MagicalRecord обеспечивает хороший способ удаления объекта, который автоматически удаляет объекты из ManagedObjectContext. После удаления, пользователь вернется к основному списку пива.


Если пользователь нажмет кнопку Done, это позволит сохранить пиво и вернуться к основному списку. Найти метод addNewBeer. Он просто отображает контроллера представления и вернется к списку. Когда view закрывается, то вызвиться метод viewWillDisapper:. Это, в свою очередь сделает вызов callssaveContext.


Прямо сейчас метод saveContext пуст, так что Вам потребуется добавить некоторый код, чтобы сохранить объект. Добавьте следующий код в метод saveContext:


- (void)saveContext {
    [[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
        if (success) {
            NSLog(@"You successfully saved your context.");
        } else if (error) {
            NSLog(@"Error saving context: %@", error.description);
        }
    }];
}


Здесь всего несколько строк кода! В AppDelegate.m, Вы настраиваете основные данные стека WithMagicalRecord. Это создало доступ к приложение по-умолчанию с managedObjectContext. При создании объектов Beer и BeerDetails, их добавили в defaultContext. MagicalRecord позволяет сохранить managedObjectContext, с помощью saveToPersistentStoreWithCompletion:. Завершение блока дает Вам доступ к объекту NSError, в случает, если не удалось сохранить. Здесь, Вы добавили простой if/else блок, который регистрирует на то, что происходит после того, как Вы сохраните defaultContext.


Вы готовы, проверить свое творение? Теперь, запустите свое приложение! Нажмите кнопку "+", заполните любую информацию, которую Вы хотите. Затем нажмите «готово».

image

После того, как Вы сделаете это, Вы заметите, что новое пиво не отображается в списке. Не волнуйтесь, оно не сломано, и Вы узнаете, как это поправить это все чуть позже. Вы также могли заметить, что отладчик имеет целую кучу информации. Вопреки тому, что Ваша логика может сказать, это действительно неплохо.

image

Волшебный отладчик

Когда приложение запущено, MagicalRecord вводит четыре вещи при входе в Core Data стек установки. Это показывает, что настройка Core Data произошла, и что defaultContext был создан. Тот же defaultContext, который упоминался выше, когда Вы сохранили объект Beer.

После того как Вы нажимаете кнопку Done и сохраняете MagicalRecord появляются еще несколько протоколов. В процессе сохранения было произведено такие протоколы:

  1. В defaultContext сохранена основная нить.
  2. Все исходные материалы будут сохранены и отмечены флажком 1.
  3. Синхронизация не будет проходить одновременно и все сохраненные файлы будут отмечены флажком 0.
  4. Окончательные протоколы показывают, что MagicalRecord знает, о двух объектах, которые должны быть сохранены (объекты Beer и BeerDetails), и что они успешно сохранены.


MagicalRecord протоколирует большое количество информации. Если у вас возникли проблемы, или что-то не работает должным образом, Вы должны проверить ваши протоколы, т.к. они несут много полезной информации.

Примечание:
Хотя я не рекомендую, но если вы действительно не хотите видеть что происходит, когда протоколы MagicalRecord, войдите в MagicalRecord.h и замените строку 17:

#define MR_ENABLE_ACTIVE_RECORD_LOGGING 1

на
#define MR_ENABLE_ACTIVE_RECORD_LOGGING 0


Еще одно пиво, пожалуйста!

Если это приложение стоит всех затраченных усилий, Вы сможете просматривать все сорты пива, которые Вы добавили. Открыть MasterViewController.m, и импортировать Beer.h, и BeerDetails.h.

Для того, чтобы получить все сорта пива сохранены в CoreData, Вы должны будете сделать выборку. Добавьте выборку В viewWillAppear: перед вызовом метода reloadData.


- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // Check if the user's sort preference has been saved.
    ...
    [self fetchAllBeers];
    [self.tableView reloadData];
}


При первом просмотре загрузок, или при возвращении после просмотра или добавления Пива, стоит загрузить все виды пива в таблицу.

Найдите метод fetchAllBeers, и добавьте следующее:

- (void)fetchAllBeers {
    // 1. Get the sort key
    NSString *sortKey = [[NSUserDefaults standardUserDefaults] objectForKey:WB_SORT_KEY];
    // 2. Determine if it is ascending
    BOOL ascending = [sortKey isEqualToString:SORT_KEY_RATING] ? NO : YES;
    // 3. Fetch entities with MagicalRecord
    self.beers = [[Beer findAllSortedBy:sortKey ascending:ascending] mutableCopy];
}


MasterViewController позволяет пользователю сортировать пиво по рейтингу с оценкой от 5 до 1 за пиво, или в алфавитном порядке (A-Я). Первый раз, когда приложение запускается, оно создает NSUserDefault (значение сортировки по рейтингу) и устанавливает его по умолчанию. В таком случае Вы:

  1. Восстановленный ключ сортировки хранится в NSUserDefaults
  2. Если ключ сортировки-это “рейтинг”, то возрастанию переменного значения нужно отметить как " нет". Если по алфавиту, то значение — " да".
  3. Выполняеться fetch.


Да, это действительно все, что нужно сделать!

image

Еще раз, Вы используете MagicalRecord как способ взаимодействия с Core Data. findAllSortedBy: по возрастанию-это лишь один из многих способов выполнения выборки основных данных субъектов, использующих MagicalRecord. Некоторые другие включают (обратите внимание — вы должны будете использовать один из них позже):

  • indAllInContext: — найдете все объекты типа в контексте, при условии,
  • findAll — найдете все объекты в контекст текущего потока
  • findAllSortedBy: по возрастанию:inContext: — подобно той, что использовалась ранее, но ограничивается их-ха, контекста
  • findAllWithPredicate: — позволяет перейти в NSPredicate для поиска объектов.
  • findAllSortedBy: по возрастанию:withPredicate:inContext: — позволяет сортировать сделанное, с отметкой флагом, в определенном контексте. Это также позволяет Вам передавать в NSPredicate для фильтрации.


Есть многое другое, чем вы можете воспользоваться — просто проверьте NSManagedObject+MagicalFinders.m.

Чтобы название пива и рейтинг отобразить в ячейке, следуйте configureCell:atIndex:, и добавьте следующее:

- (void)configureCell:(UITableViewCell*)cell atIndex:(NSIndexPath*)indexPath {
    // Get current Beer
    Beer *beer = self.beers[indexPath.row];
    cell.textLabel.text = beer.name;	
    // Setup AMRatingControl
    AMRatingControl *ratingControl;
    if (![cell viewWithTag:20]) {
        ratingControl = [[AMRatingControl alloc] initWithLocation:CGPointMake(190, 10) emptyImage:[UIImage imageNamed:@"beermug-empty"] solidImage:[UIImage imageNamed:@"beermug-full"] andMaxRating:5];
        ratingControl.tag = 20;
        ratingControl.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
        ratingControl.userInteractionEnabled = NO;
        [cell addSubview:ratingControl];
    } else {
        ratingControl = (AMRatingControl*)[cell viewWithTag:20];
    }
    // Put beer rating in cell
    ratingControl.rating = [beer.beerDetails.rating integerValue];
}


А сейчас найдите метод prepareForSegue:sender:, и в if который проверяет, является ли переход идентификатором “editBeer,” добавьте:

if ([[segue identifier] isEqualToString:@"editBeer"]) {
    NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    Beer *beer = self.beers[indexPath.row];
    upcoming.beer = beer;
}


Это передавать объект Beer в BeerViewController, чтобы он отображал сведения о пиве, позволяет Вам его редактировать.

Попробуйте запустить проект еще раз.

Теперь Вы увидите пиво, которое добавили ранее, с его рейтингом. Также, Вы можете выбрать пиво и редактировать информацию. Когда Вы вернетесь, таблица будет обновлена! Очень хорошо!

image

Попробуйте просмотреть объект Beer по-отдельности, и без редактирования информации, вернуться к основному списку. Взгляните на журнал. Вы должны увидеть:

-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x8b6bfa0) NO CHANGES IN ** DEFAULT ** CONTEXT - NOT SAVING


Когда Вы закончите просмотр деталей, код, который Вы написали, будет сохранен в контексте по умолчанию в viewWillDisappear:. Однако, поскольку были внесены изменения, MagicalRecord признает, что нет необходимости выполнять операцию сохранения, и поэтому он пропускает процесс. Благо в этом нет необходимости для, думать о том, нужно ли Вам сохранить — просто попробуйте и сохранить, и пусть MagicalRecord сделает это для вас.

Последние штрихи

Есть несколько функций, которые Вы, наверняка, захотите добавить в свое приложение, например — возможность удалять пиво, предварительно заполнив список любимых сортов пива и выполнив поиск.

Удаление

В MasterViewController.m, найти метод делегата tableView:commitEditingStyle:forRowAtIndexPath:, и добавьте следующий код:


- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        Beer *beerToRemove = self.beers[indexPath.row];
        // Remove Image from local documents
        if (beerToRemove.beerDetails.image) {
            [ImageSaver deleteImageAtPath:beerToRemove.beerDetails.image];
        }
        // Deleting an Entity with MagicalRecord
        [beerToRemove deleteEntity];
        [self saveContext];
        [self.beers removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}


Обратите внимание, есть специальный вызов saveContext. Вам необходимо добавить код и убедиться, что удаление завершилось. Если Вы уже сделали это один раз — Вы можете выяснить, как заставить его работать? Готовы......? Вперед!

Добавьте следующий код метод saveContext:

// Save ManagedObjectContext using MagicalRecord
[[NSManagedObjectContext defaultContext] saveToPersistentStoreAndWait];


Поскольку технически Вам не нужно знать, когда он закончится, вы можете использовать одину из возможностей MagicalRecord сохранить managedObjectContext.

Запустите приложение и удалите пиво (традиционно используя ячейки). Если Вы повторно запустите приложение, а пива там не оказалось, Вы сделали что-то очень нужное! Вы сохранили изменения, а это означает, что Вы использовали MagicalRecord правильно.

Похлопайте себе!


Демо-Данные

Было бы неплохо дать пользователям некоторые исходные данные, чтобы они могли видеть, как легко они могут отслеживать свои любимые сорта. В файле AppDelegate.m, сделайте импорт файлов Beer.h, BeerDetails.h. Затем, сразу после установки Core Data стека, добавьте следующее:

// Setup App with prefilled Beer items.
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"MR_HasPrefilledBeers"]) {
    // Create Blond Ale
    Beer *blondAle = [Beer createEntity];
    blondAle.name  = @"Blond Ale";
    blondAle.beerDetails = [BeerDetails createEntity];
    blondAle.beerDetails.rating = @4;
    [ImageSaver saveImageToDisk:[UIImage imageNamed:@"blond.jpg"] andToBeer:blondAle];
 
    // Create Wheat Beer
    Beer *wheatBeer = [Beer createEntity];
    wheatBeer.name  = @"Wheat Beer";
    wheatBeer.beerDetails = [BeerDetails createEntity];
    wheatBeer.beerDetails.rating = @2;
    [ImageSaver saveImageToDisk:[UIImage imageNamed:@"wheat.jpg"] andToBeer:wheatBeer];
 
    // Create Pale Lager
    Beer *paleLager = [Beer createEntity];
    paleLager.name  = @"Pale Lager";
    paleLager.beerDetails = [BeerDetails createEntity];
    paleLager.beerDetails.rating = @3;
    [ImageSaver saveImageToDisk:[UIImage imageNamed:@"pale.jpg"] andToBeer:paleLager];
 
    // Create Stout
    Beer *stout = [Beer createEntity];
    stout.name  = @"Stout Lager";
    stout.beerDetails = [BeerDetails createEntity];
    stout.beerDetails.rating = @5;
    [ImageSaver saveImageToDisk:[UIImage imageNamed:@"stout.jpg"] andToBeer:stout];
 
    // Save Managed Object Context
    [[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:nil];
 
    // Set User Default to prevent another preload of data on startup.
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MR_HasPrefilledBeers"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}


Стартовое приложение, которое Вы скачали в начале этой статьй, включает в себя четыре изображения холодного пива. Тут Вы можете не просто создаете четыре разных сорта пива, но и сохраняете их. Сохраните флажки в NSUserDefaults разрешая приложению предварительно заполнять DataModel только один раз, когда он запускается впервые.

Снова запустите приложение, так чтобы Вы могли видеть все новые сорта пива. Попробуйте удалить один и повторного запустить приложение; оно не вернется. Если вы хотите увидеть все образцы пива снова, удалите приложение в симулятора или устройства и запустите заново.


image

Поиск

Теперь, когда у вас больше одного сорта пива, протестируйте возможности поиска. Стартовое приложение уже включена в строке поиска — выделите верхнюю часть таблицы, чтобы увидеть его. Единственное, что нужно добавить логику, для поиска в списке пива.

Ранее Вы использовали MagicalRecord как вспомогательный метод, чтобы сделать выборку всех сортов пива, и просто возращали все пиво. Итак, если Вы хотите получить все сорта пива, которые соответствуют определенному условию поиска.
Для этого вам нужно будет использовать NSPredicate. Ранее в пособии говорилось о методе выборки с NSPredicate. Можете Вы догадались, как сделать поиск? Логика должна быть внутри doSearch в MasterViewController.m file.

Добавьте следующий код в метод doSearch:

- (void)doSearch {
	// 1. Get the text from the search bar.
	NSString *searchText = self.searchBar.text;
	// 2. Do a fetch on the beers that match Predicate criteria.
	// In this case, if the name contains the string
	self.beers = [[Beer findAllSortedBy:SORT_KEY_NAME ascending:YES withPredicate:[NSPredicate predicateWithFormat:@"name contains[c] %@", searchText] inContext:[NSManagedObjectContext defaultContext]] mutableCopy];
	// 3. Reload the table to show the query results.
	[self.tableView reloadData];
}


Для других методов для извлечения результатов, проверить файлы заголовков MagicalRecord.

Снова запустите приложение и проскрольте список пива вниз, до тех пор, пока строка поиска не появиться. Ищите один из сортов пива, в списке, затем найдите тот, который не в списке. Получается ли у вас добиться желаемого результата?

image

Что дальше?

Надеюсь, эта статья о MagicalRecord показало Вам, как легко работать с MagicalRecord. Это действительно помогает сократить количество написанного кода! Основы, которые Вы изучили в этом уроке помогут Вам в разработке всех видов приложений, которые помогают пользователям следить за вещами, так как позволяет использовать картинки, заметки и рейтинги. Наслаждайтесь!

Вы можете скачать готовый проект здесь. Он может пригодиться, если на каком-то этапы возникли трудности.

Если Вы хотите развивать проект BeerTracker, ниже приводятся несколько идей, которые помогут Вам развить это приложение:

  • Добавить сообщение “нет пива, созданных еще” на MasterViewController — использование поиск hasAtLeastOneEntity метод в MagicalRecord.
  • Добавить сообщение, указывающее, сколько пива есть для поиска. для контроллера поиска — используйте метод countOfEntitiesWithPredicate:.
  • Реализации баз данных функция reset — метод поиска truncateAll от MagicalRecord
  • Для удовольствия открыть файл MagicalRecordShorthand.h и прочитать имена методов — большинство из них довольно понятны, этот заголовочный файл должен дать вам еще больше идей, как использовать MagicalRecord
Tags:
Hubs:
+8
Comments 5
Comments Comments 5

Articles