Tumgik
#magicalrecord
ninthpathblog · 10 years
Text
Restkit and MagicalRecord
Recently, I worked on a project where the app had to download a lot of JSON data and store the results in Core Data.
I decided this was a good time to try out some frameworks, so I installed the pods for RestKit and MagicalRecord.  
With RestKit, you make a mapping, like:
@{@"job_name":@"jobName"}
Then you pass that on to the framework, along with the URL and other info, and it automatically downloads, parses, and saves that data into Core Data.
MagicalRecord also has convenience methods for importing JSON into Core Data.  Just follow the instructions in their documentation.  You go into your data model, and populate the User Info (right hand side).  Let's say you have a field called "displayName" and the JSON object has a "display_name" field. For the "displayName" field, you would add a key called "mappedKeyName" and the value would be "display_name".  
Once you've finished adding keys to the entity, to import something, you'd call:
Person *importedPerson = [Person MR_importFromObject:contactInfo];
So, just make a few edits and the framework does the rest.  Great, right?  Well, it depends.  If the JSON object is fairly simple, and you're importing 10 at a time, sure.  If the JSON object is gnarly, with multiple subarrays and dictionaries, AND you have to import 50 at a time AND establish relationships among them, not so much.  I tried both frameworks, but  I felt like I had to do too much work to shoehorn the frameworks into my code.
I ended using AFNetworking to download the JSON, then on completion, I manually looped through all the JSON dictionaries and loaded them into Core Data.  For this, I used MagicalRecord's saveWithBlock method.  While I didn't use its convenience method for importing JSON, MagicalRecord still has a lot of great utilities.
It sets up a decent NSManagedObjectContext stack for you.  The stack follows best practices, and uses a persistent store->private queue context (root)->main queue context (default).  When you save, it creates a throwaway private context, and makes it a child of the root (private) context.  Overall, MagicalRecord eliminates a lot of Core Data boilerplate.
One last thing, don't forget to use Apple's best practices for bulk imports.  Basically, if you're importing 50 JSON objects, you want to avoid doing 50 cycles of fetch/check id->create/update->save.  Instead, loop through all the objects' unique IDs, and do one fetch using an "id IN" predicate.  This gets the old records.  Then do another loop and if an ID is not an old record, you create a new one.  So that's only one fetch and one save vs. 50 fetches + saves.
0 notes
mrlarryli · 10 years
Text
Post 5 - Player Stats Screen
I did a portion of the player stats. It's the Press Start 2P font. It's only temporary, but I might keep it. This is my last update for a while. I'll be out of the country for about a month.
Tumblr media
I'm getting a better idea of how I want the game to look and feel. I finally split the loading and creating of permanent game data. I use an entirely separate program to create the data and just add the resulting data files to my main project. A good portion of the past few days was spent fixing bugs and learning new things.
1) The trim setting in Texture Packer alters the final size by removing transparency around images. In some cases, it's desirable, but in my case I planned for textures to be a certain size with the transparency. This fixed some graphical issues I was having.
2) Magical Record 3.0 is pretty awesome. These two store options were very helpful in creating and reading a permanent data file. It's easier to subclass one of the MagicalRecordStack classes and add the options in  the - (NSDIctionary *)defaultStoreOptions method
@{ NSSQLitePragmasOption : @{ @"journal_mode" : @"DELETE" }}
and
@{NSReadOnlyPersistentStoreOption: @YES}
0 notes
syshen · 11 years
Text
Key Value Observing on NSManagedObject through MagicalRecord
If you want to track the change of a NSManagedObject with MagicalRecord. Except NSFetchedResultsController, here is something you can do:
MagicalRecord's default NSManagedObjectContext will listen to the NSManagedObjectContextDidSaveNotification notification and merge the changes into its managed objects. That is why you can track the changes with objects fetched from the default context.
I think this is quite useful.
2 notes · View notes
euvs · 11 years
Text
Xcode 5, Core Data и юнит-тесты
Пришла мне как-то идея новой программы для айФона для подсчета количества явлениий и показа статистки. Ничего сложного и ничего серьезного, за недельку по вечерам думаю напишу. Интерфейс в несколько UIViews и UIViewControllers, да данные записывать в CoreData. Когда я начинал писать SunFollower, я все размышлял о том, как же юнит-тестировать CoreData. И вот вчера, пока созд��вал проект для новой идеи и разбирался c юнит-тестами, кое-какие идеи появились. О чем и собраюсь поведать. Что такое Core Data, я писать не буду. Это целая отдельная история. Буду считать, что мой читатель знаком с этой технологией.
Xcode 5 проект с юнит-тестами
Если создавать новый проект в Xcode, то он предлагает создать юнит-тесты автоматически. Но что делать если проект уже был создан без юнит-тестов, или юнит-тесты были удалены (как я сделал когда-то по-глупости)?
Юнит-тесты добавляются, если кликнуть на “Add Target..”
Tumblr media
Затем выбрать “Cocoa Touch Unit Testing Bundle”
Tumblr media
На следующем шаге я оставлю все по умолчанию, но нет ни каких проблем поменять так как удобнее вам.
Tumblr media
Светую не менять тип юнит-тестов и оставить их XCTest.
Xcode создаст новый target и добавит все необходимые новые файлы. В моем случае он добавл target “Occurrence Tests” и добавил Occurrence_Tests.m файл к проекту. Файл Occurrence_Tests.m - это загатовка для юнит-тестов:
#import <XCTest/XCTest.h> @interface Occurrence_Tests : XCTestCase @end @implementation Occurrence_Tests - (void)setUp { [super setUp]; // Put setup code here. This method is called before // the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after // the invocation of each test method in the class. [super tearDown]; } - (void)testExample { XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); } @end
Теперь можно откомпилировать тест (Product->Build For->Testing или ⇧⌘U) и запустить его (“Product->Test” или ⌘U). Тест должен выполнится с ошибкой “No implementation for “-[Occurrence_Tests testExample]”. Что и ожидаемо.
Кстати, очень полезная вкладка “Test Navigator”, которая показывает список всех тестов и результат их выполнения (для наглядности я добавил еще три теста).
Tumblr media
Если навести курсор мышки на один из юнит тестов, то появится маленька кнопка Play ( ▶ ), с помощью которой можно выборочно запускать отдельные тесты на исполнение.
Tumblr media
Загатовка для юнит-тестов готова.
CocoaPods и MagicalRecord
Следующий шаг. Устанавливаем MagicalRecord через CocoaPods. Надеюсь вы используете CocoaPods. Нет? о_0 ?! Ок.
CocoaPods, как утверждают разработчики, - это “The best way to manage library dependencies in Objective-C projects.” И они действительно правы - это самый лучший способ подключать библиотеки к проекту. Советую заглянуть на сайт http://cocoapods.org.
Устанавливаем cocoapods через консоль:
$ sudo gem install cocoapods $ pod setup
MagicalRecord - это такая волшебная библиотека, которая превращает работу с CoreData в прогулку по парку. Вместо того чтобы в ручную устанавливать и добавлять библиотеку к проекту для этого используем cocoapods. Для этого в папке, где находится наш проект “.xcodeproj”, создаем файл с названием “Podfile”. Содержание Podfile-файла:
platform :ios, '7.0' pod 'MagicalRecord', '2.2' link_with ['Occurrence', 'Occurrence Tests']
Не забудьте заменить Occurrence и Occurrence Tests, на название target-ов вашего проекта.
После этого в консоле переходим в папку с проектом (там где Podfile) и пишем:
$ pod install
Cocoapods создаст воркспейс “.xcworkspace” файл, подключит к нему проект, который мы создали, создаст новый проект под названием Pods, скачает все необходимые библиотеки для MagicalRecord и сконфигурирует все проекты как надо. Впредь основным файлом будет xcworkspace-файл.
Tumblr media
Теперь в проекте есть юнит-тесты и MagicalRecord библоетка подключенная через Pods
Core Data, MagicalRecord и юнит-тесты.
Все необходимое готово, теперь переходим к юнит-тестам для Core Data.
Открываем наш тест файл Occurrence_Tests.m и добавляем header
#import "CoreData+MagicalRecord.h"
Инициализируем Core Data стэк в методе (void)setUp
- (void)setUp { [super setUp]; [MagicalRecord setupCoreDataStackWithInMemoryStore]; }
Метод (void)tearDown, соответсвенно, служит для удаления всех ресурсов в конце теста.
- (void)tearDown { [MagicalRecord cleanUp]; [super tearDown]; }
[MagicalRecord setupCoreDataStackWithInMemoryStore] инициализирует CoreData стэк с типом NSInMemoryStoreType. Одна строчка кода вместо того, чтобы в ручную расписывать всю инициализацию. Magic!
Теперь пишем юнит-тесты.
Вот кусок модели (model) Core Data
Tumblr media
А вот, тест, который создает Project и добавляет к нему Question
- (void)testProjectCreation { NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext]; Project *prj = [Project MR_createEntity]; prj.name = @"Lifts"; Question *q1 = [Question MR_createEntity]; q1.name = @"UP/DOWN?"; [prj addQuestionsObject:q1]; // Save the context. [context MR_saveOnlySelfWithCompletion:^(BOOL success, NSError *error) { if (!success) { XCTFail(@"%s, Unresolved error %@, %@", __PRETTY_FUNCTION__, error, [error userInfo]); } }]; }
А дальше ⌘U. :)
И что же я раньше этого не делал?! :)
Ссылки для чтения
http://cocoapods.org/
https://github.com/magicalpanda/MagicalRecord
Сессия 409 на WWDC 2013 “Testing in Xcode 5” - https://developer.apple.com/wwdc/videos/index.php?id=409 (нужен девелоперский аккаунт)
Core Data: Data Storage and Management for iOS, OS X, and iCloud (Pragmatic Programmers), by Marcus S. Zarra Будьте внимательны. Второе издание. Первое издане сильно устарело.
1 note · View note