From 0c35d9f81d518edd17d79a7f7a3eb5573477ea9d Mon Sep 17 00:00:00 2001 From: Tony Arnold Date: Sun, 2 Nov 2014 01:21:37 +1100 Subject: [PATCH] Lots of updates to the documentation from the wiki (and some new content, too) --- .cocoadocs.yml | 2 +- CONTRIBUTING.md | 2 +- Docs/Creating-Entities.md | 13 ++ Docs/DefaultManagedObjectContext.md | 30 ---- Docs/Deleting-Entities.md | 25 +++ Docs/Fetching-Entities.md | 131 ++++++++++++++ Docs/Fetching.md | 150 ---------------- .../{GettingStarted.md => Getting-Started.md} | 17 +- Docs/Importing.md | 168 +++++++++++++++++- Docs/Installation.md | 17 -- Docs/Installing-MagicalRecord.md | 67 +++++++ Docs/Logging.md | 12 +- Docs/{Resources.md => Other-Resources.md} | 5 +- Docs/{Saving.md => Saving-Entities.md} | 72 +++++--- Docs/Support.md | 44 ----- Docs/Working-with-Managed-Object-Contexts.md | 85 +++++++++ README.md | 39 ++-- 17 files changed, 581 insertions(+), 298 deletions(-) create mode 100644 Docs/Creating-Entities.md delete mode 100644 Docs/DefaultManagedObjectContext.md create mode 100644 Docs/Deleting-Entities.md create mode 100644 Docs/Fetching-Entities.md delete mode 100644 Docs/Fetching.md rename Docs/{GettingStarted.md => Getting-Started.md} (85%) delete mode 100644 Docs/Installation.md create mode 100644 Docs/Installing-MagicalRecord.md rename Docs/{Resources.md => Other-Resources.md} (83%) rename Docs/{Saving.md => Saving-Entities.md} (68%) delete mode 100644 Docs/Support.md create mode 100644 Docs/Working-with-Managed-Object-Contexts.md diff --git a/.cocoadocs.yml b/.cocoadocs.yml index 642a26f9f..41ffd566a 100644 --- a/.cocoadocs.yml +++ b/.cocoadocs.yml @@ -1,5 +1,5 @@ additional_guides: - - https://github.com/magicalpanda/MagicalRecord/wiki/Installation + - https://github.com/magicalpanda/MagicalRecord/wiki/Installing-MagicalRecord - https://github.com/magicalpanda/MagicalRecord/wiki/Getting-Started - https://github.com/magicalpanda/MagicalRecord/wiki/Working-with-Managed-Object-Contexts - https://github.com/magicalpanda/MagicalRecord/wiki/Creating-Entities diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c879bf776..57a363561 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,4 +22,4 @@ Fork this repository, make some great changes (preferably in a branch named for All code contributions should match our [coding conventions](/magicalpanda/MagicalRecord/wiki/Coding-Conventions). -Thanks for reading the guidelines! \ No newline at end of file +Thanks for reading the guidelines! diff --git a/Docs/Creating-Entities.md b/Docs/Creating-Entities.md new file mode 100644 index 000000000..d497e27ef --- /dev/null +++ b/Docs/Creating-Entities.md @@ -0,0 +1,13 @@ +# Creating Entities + +To create and insert a new instance of an Entity in the default context, you can use: + +```objective-c +Person *myPerson = [Person MR_createEntity]; +``` + +To create and insert an entity into specific context: + +```objective-c +Person *myPerson = [Person MR_createEntityInContext:otherContext]; +``` diff --git a/Docs/DefaultManagedObjectContext.md b/Docs/DefaultManagedObjectContext.md deleted file mode 100644 index 3a729b5a5..000000000 --- a/Docs/DefaultManagedObjectContext.md +++ /dev/null @@ -1,30 +0,0 @@ -### Default Managed Object Context - -When working with Core Data, you will regularly deal with two main objects: `NSManagedObject` and `NSManagedObjectContext`. - -MagicalRecord provides a simple class method to retrieve a default `NSManagedObjectContext` that can be used throughout your app. This context operates on the main thread, and is great for simple, single-threaded apps. - -To access the default context, call: - -```objective-c -NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext]; -``` - -This context will be used throughout MagicalRecord in any method that uses a context, but does not provde a specific managed object context parameter. - -If you need to create a new managed object context for use in non-main threads, use the following method: - -```objective-c -NSManagedObjectContext *myNewContext = [NSManagedObjectContext MR_newContext]; -``` - -This will create a new managed object context which has the same object model and persistent store as the default context, but is safe for use on another thread. It automatically sets the default context as it's parent context. - -If you'd like to make your `myNewContext` instance the default for all fetch requests, use the following class method: - -```objective-c -[NSManagedObjectContext MR_setDefaultContext:myNewContext]; -``` - -> **NOTE:** It is *highly* recommended that the default context is created and set on the main thread using a managed object context with a concurrency type of `NSMainQueueConcurrencyType`. - diff --git a/Docs/Deleting-Entities.md b/Docs/Deleting-Entities.md new file mode 100644 index 000000000..1c1715ba1 --- /dev/null +++ b/Docs/Deleting-Entities.md @@ -0,0 +1,25 @@ +# Deleting Entities + +To delete a single entity in the default context: + +```objective-c +[myPerson MR_deleteEntity]; +``` + +To delete the entity from a specific context: + +```objective-c +[myPerson MR_deleteEntityInContext:otherContext]; +``` + +To truncate all entities from the default context: + +```objective-c +[Person MR_truncateAll]; +``` + +To truncate all entities in a specific context: + +```objective-c +[Person MR_truncateAllInContext:otherContext]; +``` diff --git a/Docs/Fetching-Entities.md b/Docs/Fetching-Entities.md new file mode 100644 index 000000000..448b33693 --- /dev/null +++ b/Docs/Fetching-Entities.md @@ -0,0 +1,131 @@ +# Fetching Entities + +> This document is being revised for MagicalRecord 2.3.0, and may contain information that is out of date. Please refer to the MagicalRecord's headers if anything here doesn't make sense. + +#### Basic Finding + +Most methods in MagicalRecord return an `NSArray` of results. + +As an example, if you have an entity named *Person* related to a *Department* entity (as seen in many examples throughout [Apple's Core Data documentation)[https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/), you can retrieve all of the *Person* entities from your persistent store using the following method: + +```objective-c +NSArray *people = [Person MR_findAll]; +``` + +To return the same entities sorted by a specific attribute: + +```objective-c +NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName" + ascending:YES]; +``` + +To return the entities sorted by multiple attributes: + +```objective-c +NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName" + ascending:YES]; +``` + +To return the results sorted by multiple attributes with different values. If you don't provide a value for any attribute, it will default to whatever you've set in your model: + +```objective-c +NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName:NO,FirstName" + ascending:YES]; + +// OR + +NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName:YES" + ascending:NO]; +``` + +If you have a unique way of retrieving a single object from your data store (such as an identifier attribute), you can use the following method: + +```objective-c +Person *person = [Person MR_findFirstByAttribute:@"FirstName" + withValue:@"Forrest"]; +``` + +#### Advanced Finding + +If you want to be more specific with your search, you can use a predicate: + +```objective-c +NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", @[dept1, dept2]]; +NSArray *people = [Person MR_findAllWithPredicate:peopleFilter]; +``` + +#### Returning an NSFetchRequest + +```objective-c +NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments]; +NSFetchRequest *people = [Person MR_requestAllWithPredicate:peopleFilter]; +``` + +For each of these single line calls, an `NSFetchRequest` and `NSSortDescriptor`s for any sorting criteria are created. + +#### Customizing the Request + +```objective-c +NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments]; + +NSFetchRequest *peopleRequest = [Person MR_requestAllWithPredicate:peopleFilter]; +[peopleRequest setReturnsDistinctResults:NO]; +[peopleRequest setReturnPropertiesNamed:@[@"FirstName", @"LastName"]]; + +NSArray *people = [Person MR_executeFetchRequest:peopleRequest]; +``` + +#### Find the number of entities + +You can also perform a count of all entities of a specific type in your persistent store: + +```objective-c +NSNumber *count = [Person MR_numberOfEntities]; +``` + +Or, if you're looking for a count of entities based on a predicate or some filter: + +```objective-c +NSNumber *count = [Person MR_numberOfEntitiesWithPredicate:...]; +``` + +There are also complementary methods which return `NSUInteger` rather than `NSNumber` instances: + +```objective-c ++ (NSUInteger) MR_countOfEntities; ++ (NSUInteger) MR_countOfEntitiesWithContext:(NSManagedObjectContext *)context; ++ (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter; ++ (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter + inContext:(NSManagedObjectContext *)context; +``` + +#### Aggregate Operations + +```objective-c +NSNumber *totalCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" + onAttribute:@"calories" + withPredicate:predicate]; + +NSNumber *mostCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"max:" + onAttribute:@"calories" + withPredicate:predicate]; + +NSArray *caloriesByMonth = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" + onAttribute:@"calories" + withPredicate:predicate + groupBy:@"month"]; +``` + +#### Finding entities in a specific context + +All find, fetch, and request methods have an `inContext:` method parameter that allows you to specify which managed object context you'd like to query: + +```objective-c +NSArray *peopleFromAnotherContext = [Person MR_findAllInContext:someOtherContext]; + +Person *personFromContext = [Person MR_findFirstByAttribute:@"lastName" + withValue:@"Gump" + inContext:someOtherContext]; + +NSUInteger count = [Person MR_numberOfEntitiesWithContext:someOtherContext]; +``` diff --git a/Docs/Fetching.md b/Docs/Fetching.md deleted file mode 100644 index 462527cc4..000000000 --- a/Docs/Fetching.md +++ /dev/null @@ -1,150 +0,0 @@ -### Fetching - -#### Basic Finding - -Most methods in MagicalRecord return an `NSArray` of results. - -Say you have an Entity called "Person", related to a Department (as seen in various Apple Core Data documentation). To get all of the Person entities from your Persistent Store, use the following method: - -```objective-c -NSArray *people = [Person MR_findAll]; -``` - -Or, to return the results sorted by a property: - -```objective-c -NSArray *peopleSorted = [Person MR_findAllSortedBy:@"lastName" ascending:YES]; -``` - -Or, to return the results sorted by multiple properties: - -```objective-c -NSArray *peopleSorted = [Person MR_findAllSortedBy:@"lastName,firstName" ascending:YES]; -``` - -Or, to return the results sorted by multiple properties with different attributes (these will default to whatever you set them to): - -```objective-c -NSArray *peopleSorted = [Person MR_findAllSortedBy:@"lastName:NO,firstName" ascending:YES]; - -// OR - -NSArray *peopleSorted = [Person MR_findAllSortedBy:@"lastName,firstName:YES" ascending:NO]; -``` - -If you have a unique way of retrieving a single object from your data store (such as via an identifier), you can use the following method: - -```objective-c -Person *person = [Person MR_findFirstByAttribute:@"firstName" withValue:@"Forrest"]; -``` - -#### Advanced Finding - -If you want to be more specific with your search, you can send in a predicate: - -```objective-c -NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"department IN %@", @[dept1, dept2]]; -NSArray *people = [Person MR_findAllWithPredicate:peopleFilter]; -``` - -#### Returning an NSFetchRequest - -```objective-c -NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"department IN %@", departments]; -NSFetchRequest *people = [Person MR_requestAllWithPredicate:peopleFilter]; -``` - -For each of these single line calls, the full stack of NSFetchRequest, NSSortDescriptors and a simple default error handling scheme (ie. logging to the console) is created. - -#### Customizing the Request - -```objective-c -NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"department IN %@", departments]; - -NSFetchRequest *peopleRequest = [Person MR_requestAllWithPredicate:peopleFilter]; -[peopleRequest setReturnsDistinctResults:NO]; -[peopleRequest setReturnPropertiesNamed:@[@"firstName", @"lastName"]]; - -NSArray *people = [Person MR_executeFetchRequest:peopleRequest]; -``` - -#### Find the number of entities - -You can also perform a count of all entities of a specific type in your Persistent Store: - -```objective-c -NSNumber *count = [Person MR_numberOfEntities]; -``` - -Or, if you're looking for a count of entities based on a predicate or some filter: - -```objective-c -NSNumber *count = [Person MR_numberOfEntitiesWithPredicate:...]; -``` - -There are also complementary methods which return `NSUInteger` rather than `NSNumber` instances: - -* `MR_countOfEntities` -* `MR_countOfEntitiesWithContext:(NSManagedObjectContext *)context` -* `MR_countOfEntitiesWithPredicate:(NSPredicate *)predicate` -* `MR_countOfEntitiesWithPredicate:(NSPredicate *)predicatecontext inContext:(NSManagedObjectContext *)` - -#### Aggregate Operations - -```objective-c -NSInteger totalFat = [[CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"fatCalories" withPredicate:predicate] integerValue]; -NSInteger fattest = [[CTFoodDiaryEntry MR_aggregateOperation:@"max:" onAttribute:@"fatCalories" withPredicate:predicate] integerValue]; -NSArray *caloriesByMonth = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"fatCalories" withPredicate:predicate groupBy:@"month"]; -``` - -#### Finding from a different context - -All find, fetch, and request methods have an inContext: method parameter - -```objective-c -NSArray *peopleFromAnotherContext = [Person MR_findAllInContext:someOtherContext]; - -Person *personFromContext = [Person MR_findFirstByAttribute:@"lastName" withValue:@"Gump" inContext:someOtherContext]; - -NSUInteger count = [Person MR_numberOfEntitiesWithContext:someOtherContext]; -``` - -## Creating new Entities - -When you need to create a new instance of an Entity, use: - -```objective-c -Person *myPerson = [Person MR_createEntity]; -``` - -or, to specify which context the entity is inserted into: - -```objective-c -Person *myPerson = [Person MR_createInContext:otherContext]; -``` - -## Deleting Entities - -To delete a single entity: - -```objective-c -[myPerson MR_deleteEntity]; -``` - -or, to delete the entity from a specific context: - -```objective-c -[myPerson MR_deleteInContext:otherContext]; -``` - -There is no delete *All Entities* or *truncate* operation in core data, so one is provided for you with Active Record for Core Data: - -```objective-c -[Person MR_truncateAll]; -``` - -or, to truncate all entities in a specific context: - -```objective-c -[Person MR_truncateAllInContext:otherContext]; -``` diff --git a/Docs/GettingStarted.md b/Docs/Getting-Started.md similarity index 85% rename from Docs/GettingStarted.md rename to Docs/Getting-Started.md index f4d6b7853..76ac4edae 100644 --- a/Docs/GettingStarted.md +++ b/Docs/Getting-Started.md @@ -1,9 +1,20 @@ # Getting Started -## Setting up the Core Data Stack +To get started, import the `MagicalRecord.h` header file in your project's pch file. This will allow a global include of all the required headers. -To get started, first, import the header file *CoreData+MagicalRecord.h* in your project's pch file. This will allow a global include of all the required headers. -Next, somewhere in your app delegate, in either the applicationDidFinishLaunching:(UIApplication \*) withOptions:(NSDictionary \*) method, or awakeFromNib, use **one** of the following setup calls with the **MagicalRecord** class: +If you're using CocoaPods or MagicalRecord.framework, your import should look like: + +```objective-c +#import +``` + +Otherwise, if you've added MagicalRecord's source files directly to your project, your import should be: + +```objective-c +#import "MagicalRecord.h" +``` + +Next, somewhere in your app delegate, in either the `- applicationDidFinishLaunching: withOptions:` method, or `-awakeFromNib`, use **one** of the following setup calls with the **MagicalRecord** class: ```objective-c + (void)setupCoreDataStack; diff --git a/Docs/Importing.md b/Docs/Importing.md index 70aee7919..2d49e0a83 100644 --- a/Docs/Importing.md +++ b/Docs/Importing.md @@ -1,3 +1,167 @@ -# Data Import +# Importing Data -MagicalRecord will now import data from NSObjects into your Core Data store. [Documentation](https://github.com/magicalpanda/MagicalRecord/wiki/Data-Import) for this feature is forthcoming. +> We're working on updating this documentation — thanks for your patience. +> For the moment, please refer to [Importing Data Made Easy](http://www.cimgf.com/2012/05/29/importing-data-made-easy/) at [Cocoa Is My Girlfriend](http://www.cimgf.com/). Much of this document is based upon Saul's work in that original article. +> +> MagicalRecord Team + +MagicalRecord can help import data from standard NSObject instances such as NSArray and NSDictionary directly into your Core Data store. + +It's a two step process to import data from an external source into your persistent store using MagicalRecord: + +1. **Define how the data you're importing maps to your store** using your data model (it's pretty much codeless!) +2. **Perform the data import** + + +## Define Your Import + +Data from external sources can be wildly variable in quality and structure, so we've done our best to make MagicalRecord's import processes flexible. + +**MagicalRecord can import data from any Key-Value Coding (KVC) compliant object**. We usually find people work with `NSArray` and `NSDictionary` instances, but it works just fine with any KVC compliant `NSObject` subclass. + +MagicalRecord makes use of the Xcode data modeling tool's "**User Info**" values to allow configuration of import options and mappings possible without having to edit any code. + +

+Xcode's 'User Info' group in the data modeller +

+ +> **For reference**: The user info keys and values are held in an NSDictionary that is attached to every entity, attribute and relationship in your data model, and can be accessed via the `userInfo` method on your `NSEntityDescription` instances. + +Xcode's data modelling tools give you access to this dictionary via the Data Model Inspector's "User Info" group. When editing a data model, you can open this inspector using Xcode's menus — **View > Utilities > Show Data Model Inspector**, or press ⌥⌘3 on your keyboard. + +By default, MagicalRecord will automatically try to match attribute and relationship names with the keys in your imported data. **If an attribute or relationship name in your model matches a key in your data, you don't need to do anything — the value attached to the key will be imported automatically**. + +For example, if an attribute on an entity has the name 'firstName', MagicalRecord will assume the key in the data to import will also have a key of 'firstName' — if it does, your entity's `firstName` attribute will be set to the value of the `firstName` key in your data. + +More often than not, the keys and structure in the data you are importing will not match your entity's attributes and relationships. In this case, you will need to tell MagicalRecord how to map your import data's keys to the correct attribute or relationship in your data model. + + +Each of the three key objects we deal with in Core Data — Entities, Attributes and Relationships — have options that may need to be specified via user info keys: + +### Attributes + +| Key | Type | Purpose | +|-----|------|---------| +| **attributeValueClassName** | String | TBD | +| **dateFormat** | String | TBD. Defaults to `yyyy-MM-dd'T'HH:mm:ssz`. | +| **mappedKeyName** | String | Specifies the name of the keypath in your data to import the value from. Supports keypaths, delimited by `.`, eg. `location.latitude` | +| **mappedKeyName.[0-9]** | String | Specifies backup keypath names if the key specified by **mappedKeyName** doesn't exist. Supports the same syntax. | +| **useDefaultValueWhenNotPresent** | Boolean | If this is true, the default value for the attribute will be set on the imported instance if no value is found for any key. | + +### Entities + +| Key | Type | Purpose | +|-----|------|---------| +| **relatedByAttribute** | String | Specifies the attribute in the target of the relationship that links the two. | + +### Relationships + +| Key | Type | Purpose | +|-----|------|---------| +| **mappedKeyName** | String | Specifies the name of the keypath in your data to import the value from. Supports keypaths, delimited by `.`, eg. `location.latitude` | +| **mappedKeyName.[0-9]** | String | Specifies backup keypath names if the key specified by **mappedKeyName** doesn't exist. Supports the same syntax. | +| **relatedByAttribute** | String | Specifies the attribute in the target of the relationship that links the two. | +| **type** | String | TBD | + + +## Importing Objects + +To import data into your store using MagicalRecord, you need to know two things: + +1. The format of the data you're importing, and how it + +The basic idea behind MagicalRecord's importing is that you know the entity the data should be imported into, so you then write a single line of code tying this entity with the data to import. There are a couple of options to kick off the import process. + +To automatically create a new instance from the object, you can use the following, shorter approach: + +```objective-c +NSDictionary *contactInfo = // Result from JSON parser or some other source + +Person *importedPerson = [Person MR_importFromObject:contactInfo]; +``` + +You can also use a two-stage approach: + +```objective-c +NSDictionary *contactInfo = // Result from JSON parser or some other source + +Person *person = [Person MR_createEntity]; // This doesn't have to be a new entity +[person MR_importValuesForKeysWithObject:contactInfo]; +``` + +The two-stage approach can be helpful if you’re looking to update an existing object by overwriting its attributes. + +`+MR_importFromObject:` will look for an existing object based on the configured lookup value (see the _relatedByAttribute_ and _attributeNameID_). Also notice how this follows the built in paradigm of importing a list of key-value pairs in Cocoa, as well as following the safe way to import data. + +The `+MR_importFromObject:` class method provides a wrapper around creating a new object using the previously mentioned `-MR_importValuesForKeysWithObject:` instance method, and returns the newly created object filled with data. + +A key item of note is that both these methods are synchronous. While some imports will take longer than others, it’s still highly advisable to perform *all imports* in the background so as to not impact user interaction. As previously discussed, MagicalRecord provides a handy API to make using background threads more manageable: + +```objective-c +[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *)localContext { + Person *importedPerson = [Person MR_importFromObject:personRecord inContext:localContext]; +}]; +``` + +## Importing Arrays + +It’s common for a list of data to be served using a JSON array, or you’re importing a large list of a single type of data. The details of importing such a list are taken care of in the `+MR_importFromArray:` class method. + +```objective-c +NSArray *arrayOfPeopleData = /// result from JSON parser +NSArray *people = [Person MR_importFromArray:arrayOfPeopleData]; +``` + +This method, like `+MR_importFromObject:` is also synchronous, so for background importing, use the previously mentioned helper method for performing blocks in the background. + +If your import data exactly matches your Core Data model, then read no further because the aforementioned methods are all you need to import your data into your Core Data store. However, if your data, like most, has little quirks and minor deviations, then read on, as we’ll walk through some of the features of MagicalRecord that will help you handle several commonly encountered deviations. + + +## Best Practice + +### Handling Bad Data When Importing + +APIs can often return data that has inconsistent formatting or values. The best way to handle this is to use the import category methods on your entity classes. There are three provided: + +Method | Purpose +--------------------------------|--------- +`- (BOOL) shouldImport;` | Called before an data is imported. Use this to cancel importing data on a specific instance of an entity by returning `NO`. +`- (void) willImport:(id)data;` | Called immediately before data is imported. +`- (void) didImport:(id)data;` | Called immediately after data has been imported. + + +Generally, if your data is bad you'll want to fix what the import did after an attempt has been made to import any values. + +A common scenario is importing JSON data where numeric strings can often be misinterpreted as an actual number. If you want to ensure that a value is imported as a string, you could do the following: + +```obj-c + +@interface MyGreatEntity + +@property(readwrite, nonatomic, copy) NSString *identifier; + +@end + +@implementation MyGreatEntity + +@dynamic identifier; + +- (void)didImport:(id)data +{ + if (NO == [data isKindOfClass:[NSDictionary class]]) { + return; + } + + NSDictionary *dataDictionary = (NSDictionary *)data; + + id identifierValue = dataDictionary[@"my_identifier"]; + + if ([identifierValue isKindOfClass:[NSNumber class]]) { + NSNumber *numberValue = (NSNumber *)identifierValue; + + self.identifier = [numberValue stringValue]; + } +} + +@end +``` diff --git a/Docs/Installation.md b/Docs/Installation.md deleted file mode 100644 index 04069a375..000000000 --- a/Docs/Installation.md +++ /dev/null @@ -1,17 +0,0 @@ -# Installation - -1. Drag the `MagicalRecord` folder (under the main folder) into your Xcode project; -2. Import the `CoreData+MagicalRecord.h` file in your project/target's pre-compiled header (PCH), or import the header into each class you wish to use it in; -3. Start writing code! - -**Note:** By default, all category methods added by MagicalRecord have a prefix of `MR_`. If you'd prefer to use the methods without a prefix (ie. `findAll` in lieu of `MR_findAll`) precede any instances of `#import "CoreData+MagicalRecord.h"` with `#define MR_SHORTHAND`, like so: - - #define MR_SHORTHAND - #import "CoreData+MagicalRecord.h" - - -# Requirements - -* iOS SDK 6.x or later; or -* OS X SDK 10.8 or later -* Xcode 5 is required to run the included tests, but the library builds under Xcode 4 diff --git a/Docs/Installing-MagicalRecord.md b/Docs/Installing-MagicalRecord.md new file mode 100644 index 000000000..0c3522234 --- /dev/null +++ b/Docs/Installing-MagicalRecord.md @@ -0,0 +1,67 @@ +# Installing MagicalRecord + +**Adding MagicalRecord to your project is simple**: Just choose whichever method you're most comfortable with and follow the instructions below. + +## Using CocoaPods + +The easiest way to integrate MagicalRecord in your project is to use [CocoaPods](http://cocoapods.org/): + +1. Add the following line to your `Podfile`: + + ````ruby + pod "MagicalRecord" + ```` + +2. In your project directory, run `pod update` +3. You should now be able to add `#import ` to any of your target's source files and begin using MagicalRecord! + +## Using an Xcode subproject + +Xcode sub-projects allow your project to use and build MagicalRecord as an implicit dependency. + +1. Add MagicalRecord to your project as a Git submodule: + + ```` + $ cd MyXcodeProjectFolder + $ git submodule add https://github.com/magicalpanda/MagicalRecord.git Vendor/MagicalRecord + $ git commit -m "Add MagicalRecord submodule" + ```` +2. Drag `Vendor/MagicalRecord/MagicalRecord.xcproj` into your existing Xcode project +3. Navigate to your project's settings, then select the target you wish to add MagicalRecord to +4. Navigate to **Build Phases** and expand the **Link Binary With Libraries** section +5. Click the **+** and find the version of MagicalRecord appropriate to your target's platform (`libMagicalRecord-iOS.a` for iOS, `libMagicalRecord-OSX.dylib` for OS X) +6. Navigate to **Build Settings**, then search for **Header Search Paths** and double-click it to edit +7. Add a new item using **+**: `"$(SRCROOT)/Vendor/MagicalRecord/MagicalRecord"` and ensure that it is set to *recursive* +8. You should now be able to add `#import "CoreData+MagicalRecord.h"` to any of your target's source files and begin using MagicalRecord! + +> **Note** Please be aware that if you've set Xcode's **Link Frameworks Automatically** to **No** then you may need to add the CoreData.framework to your project on iOS, as UIKit does not include Core Data by default. On OS X, Cocoa includes Core Data. + +## Manually from source + +If you don't want to use CocoaPods or use an Xcode subproject, you can add MagicalRecord's source directly to your project. + +1. Add MagicalRecord to your project as a Git submodule + + ```` + $ cd MyXcodeProjectFolder + $ git submodule add https://github.com/magicalpanda/MagicalRecord.git Vendor/MagicalRecord + $ git commit -m "Add MagicalRecord submodule" + ```` +2. Drag `Vendor/MagicalRecord/MagicalRecord` into your Xcode project, and ensure that you add it to the targets that you wish to use it with. +3. You should now be able to add `#import "CoreData+MagicalRecord.h"` to any of your target's source files and begin using MagicalRecord! + +> **Note** Please be aware that if you've set Xcode's **Link Frameworks Automatically** to **No** then you may need to add the CoreData.framework to your project on iOS, as UIKit does not include Core Data by default. On OS X, Cocoa includes Core Data. + + +# Shorthand Category Methods + +By default, all of the category methods that MagicalRecord provides are prefixed with `MR_`. This is inline with [Apple's recommendation not to create unadorned category methods to avoid naming clashes](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW4). + +If you're prepared to take the risk, and prefer the shorter method names you can do so by following setting the **MR_SHORTHAND** variable before the first time you import MagicalRecord's header into your project: + + ```objective-c +#define MR_SHORTHAND 1 +#import "CoreData+MagicalRecord.h" + ``` + +**Please note that we do not offer support for this feature**. If it doesn't work, [please file an issue](https://github.com/magicalpanda/MagicalRecord/issues/new) and we'll fix it when we can. diff --git a/Docs/Logging.md b/Docs/Logging.md index 18b7e0a30..1990e227c 100644 --- a/Docs/Logging.md +++ b/Docs/Logging.md @@ -1,6 +1,6 @@ ## Logging -MagicalRecord has logging built in to most of it's interactions with Core Data. When errors occur during fetching or saving data, these errors are captured and (if you've enabled them) logged to the console. +MagicalRecord has logging built in to most of its interactions with Core Data. When errors occur during fetching or saving data, these errors are captured and (if you've enabled them) logged to the console. Logging can be enabled by placing the following preprocessor statement before your first import of `CoreData+MagicalRecord.h`, like so: @@ -9,9 +9,11 @@ Logging can be enabled by placing the following preprocessor statement before yo #import ``` -Logging can be configured by calling `[MagicalRecord setLoggingLevel:…];` using one of the predefined logging masks: +Logging can also be enabled by passing `-DMR_LOGGING_ENABLED=1` to `OTHER_CFLAGS` (shown as "Other C Flags" in Xcode's Build Settings). If you have trouble with with the first approach, adding this build setting should work. -- **MagicalRecordLoggingLevelOff**: Don't log anything +Logging can be configured by calling `[MagicalRecord setLoggingLevel:…];` using one of the predefined logging levels: + +- **MagicalRecordLogLevelOff**: Don't log anything - **MagicalRecordLoggingLevelFatal**: Log all fatal messages - **MagicalRecordLoggingLevelError**: Log all errors and fatal messages - **MagicalRecordLoggingLevelWarn**: Log warnings, errors and fatal messages @@ -22,11 +24,11 @@ The logging level defaults to `MagicalRecordLoggingLevelWarn`. ## Disabling Logs -Setting the logging mask to **MagicalRecordLoggingLevelOff** completely disables MagicalRecord's logging. +Setting the logging level to **MagicalRecordLogLevelOff** completely disables MagicalRecord's logging. ## CocoaLumberjack -If it's available, MagicalRecord will direct it's logs to [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack). All you need to do is make sure you've imported CocoaLumberjack before you import MagicalRecord, like so: +If it's available, MagicalRecord will direct its logs to [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack). All you need to do is make sure you've imported CocoaLumberjack before you import MagicalRecord, like so: ```objective-c #import diff --git a/Docs/Resources.md b/Docs/Other-Resources.md similarity index 83% rename from Docs/Resources.md rename to Docs/Other-Resources.md index ee297233b..8a918d54a 100644 --- a/Docs/Resources.md +++ b/Docs/Other-Resources.md @@ -1,7 +1,6 @@ - # Resources -## Third Party Blog Entries -The following blog entries highlight how to install and use aspects of Magical Record. + +The following articles highlight how to install and use aspects of MagicalRecord: * [How to make Programming with Core Data Pleasant](http://yannickloriot.com/2012/03/magicalrecord-how-to-make-programming-with-core-data-pleasant/) * [Using Core Data with MagicalRecord](http://ablfx.com/blog/2012/03/using-coredata-magicalrecord/) diff --git a/Docs/Saving.md b/Docs/Saving-Entities.md similarity index 68% rename from Docs/Saving.md rename to Docs/Saving-Entities.md index e8474eb5e..5603766c9 100644 --- a/Docs/Saving.md +++ b/Docs/Saving-Entities.md @@ -1,6 +1,6 @@ -# Magical Saving +# Saving Entities -## When to save +## When should I save? In general, your app should save to it's persistent store(s) when data changes. Some applications choose to save on application termination, however this shouldn't be necessary in most circumstances — in fact, **if you're only saving when your app terminates, you're risking data loss**! What happens if your app crashes? The user will lose all the changes they've made — that's a terrible experience, and easily avoided. @@ -9,8 +9,9 @@ If you find that saving is taking a long time, there are a couple of things you 1. **Save in a background thread**: MagicalRecord provides a simple, clean API for making changes to your entities and subsequently saving them in a background thread — for example: ````objective-c [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) { + // Do your work to be saved here, against the `localContext` instance - // Everything you do in this block will occur on a background thread + // Everything you do in this block will occur on a background thread } completion:^(BOOL success, NSError *error) { [application endBackgroundTask:bgTask]; @@ -18,10 +19,10 @@ If you find that saving is taking a long time, there are a couple of things you }]; ```` -2. **Break the task into smaller saves**: tasks like importing lots of data should always be broken down into smaller chunks. There's no one-size-fits all rule for how much data you should be saving in one go, so you'll need to measure your application's performance using a tool like Apple's Instruments. +2. **Break the task down into smaller saves**: tasks like importing large amounts of data should always be broken down into smaller chunks. There's no one-size-fits all rule for how much data you should be saving in one go, so you'll need to measure your application's performance using a tool like Apple's Instruments and tune appropriately. -## Handling long-running saves +## Handling Long-running Saves ### On iOS @@ -36,6 +37,7 @@ __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWith }]; [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) { + // Do your work to be saved here } completion:^(BOOL success, NSError *error) { @@ -44,11 +46,11 @@ __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWith }]; ```` -Be sure to carefully [read the documentation for `beginBackgroundTaskWithExpirationHandler`](https://developer.apple.com/library/iOS/documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithExpirationHandler:), as inappropriately or unnecessarily extending your application's lifetime may earn your app a rejection. +Be sure to carefully [read the documentation for `beginBackgroundTaskWithExpirationHandler`](https://developer.apple.com/library/iOS/documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithExpirationHandler:), as inappropriately or unnecessarily extending your application's lifetime may earn your app a rejection from the App Store. ### On OS X -On OS X Mavericks (10.9) and later, App Nap can cause your application to be effectively terminated when it is in the background. If you know that a save operation is likely to take a while, the best approach is to disable automatic and sudden termination temporarily (assuming that your app supports these features): +On OS X Mavericks (10.9) and later, App Nap can cause your application to act as though it is effectively terminated when it is in the background. If you know that a save operation is likely to take a while, the best approach is to disable automatic and sudden termination temporarily (assuming that your app supports these features): ````objective-c NSProcessInfo *processInfo = [NSProcessInfo processInfo]; @@ -57,6 +59,7 @@ NSProcessInfo *processInfo = [NSProcessInfo processInfo]; [processInfo disableAutomaticTermination:@"Application is currently saving to persistent store"]; [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) { + // Do your work to be saved here } completion:^(BOOL success, NSError *error) { @@ -111,35 +114,46 @@ In MagicalRecord 2.2, the APIs for saving were revised to behave more consistent The following methods have been added: #### NSManagedObjectContext+MagicalSaves -- `- (void) MR_saveOnlySelfWithCompletion:(MRSaveCompletionHandler)completion;` -- `- (void) MR_saveToPersistentStoreWithCompletion:(MRSaveCompletionHandler)completion;` -- `- (void) MR_saveOnlySelfAndWait;` -- `- (void) MR_saveToPersistentStoreAndWait;` -- `- (void) MR_saveWithOptions:(MRSaveContextOptions)mask completion:(MRSaveCompletionHandler)completion;` + +```objective-c +- (void) MR_saveOnlySelfWithCompletion:(MRSaveCompletionHandler)completion; +- (void) MR_saveToPersistentStoreWithCompletion:(MRSaveCompletionHandler)completion; +- (void) MR_saveOnlySelfAndWait; +- (void) MR_saveToPersistentStoreAndWait; +- (void) MR_saveWithOptions:(MRSaveContextOptions)mask completion:(MRSaveCompletionHandler)completion; +``` #### __MagicalRecord+Actions__ -- `+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block;` -- `+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;` -- `+ (void) saveWithBlockAndWait:(void(^)(NSManagedObjectContext *localContext))block;` -- `+ (void) saveUsingCurrentThreadContextWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;` -- `+ (void) saveUsingCurrentThreadContextWithBlockAndWait:(void (^)(NSManagedObjectContext *localContext))block;` + +```objective-c ++ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block; ++ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion; ++ (void) saveWithBlockAndWait:(void(^)(NSManagedObjectContext *localContext))block; ++ (void) saveUsingCurrentThreadContextWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion; ++ (void) saveUsingCurrentThreadContextWithBlockAndWait:(void (^)(NSManagedObjectContext *localContext))block; +``` ### Deprecations The following methods have been deprecated in favour of newer alternatives, and will be removed in MagicalRecord 3.0: #### NSManagedObjectContext+MagicalSaves -- `- (void) MR_save;` -- `- (void) MR_saveWithErrorCallback:(void(^)(NSError *error))errorCallback;` -- `- (void) MR_saveInBackgroundCompletion:(void (^)(void))completion;` -- `- (void) MR_saveInBackgroundErrorHandler:(void (^)(NSError *error))errorCallback;` -- `- (void) MR_saveInBackgroundErrorHandler:(void (^)(NSError *error))errorCallback completion:(void (^)(void))completion;` -- `- (void) MR_saveNestedContexts;` -- `- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *error))errorCallback;` -- `- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *error))errorCallback completion:(void (^)(void))completion;` + +```objective-c +- (void) MR_save; +- (void) MR_saveWithErrorCallback:(void(^)(NSError *error))errorCallback; +- (void) MR_saveInBackgroundCompletion:(void (^)(void))completion; +- (void) MR_saveInBackgroundErrorHandler:(void (^)(NSError *error))errorCallback; +- (void) MR_saveInBackgroundErrorHandler:(void (^)(NSError *error))errorCallback completion:(void (^)(void))completion; +- (void) MR_saveNestedContexts; +- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *error))errorCallback; +- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *error))errorCallback completion:(void (^)(void))completion; +``` ### MagicalRecord+Actions -- `+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block;` -- `+ (void) saveInBackgroundWithBlock:(void(^)(NSManagedObjectContext *localContext))block;` -- `+ (void) saveInBackgroundWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(void(^)(void))completion;` -- `+ (void) saveInBackgroundUsingCurrentContextWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(void (^)(void))completion errorHandler:(void (^)(NSError *error))errorHandler;` +```objective-c ++ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block; ++ (void) saveInBackgroundWithBlock:(void(^)(NSManagedObjectContext *localContext))block; ++ (void) saveInBackgroundWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(void(^)(void))completion; ++ (void) saveInBackgroundUsingCurrentContextWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(void (^)(void))completion errorHandler:(void (^)(NSError *error))errorHandler; +``` diff --git a/Docs/Support.md b/Docs/Support.md deleted file mode 100644 index 2bd447b31..000000000 --- a/Docs/Support.md +++ /dev/null @@ -1,44 +0,0 @@ -##License - - Copyright (c) 2010-2013, Magical Panda Software, LLC - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - -- Provide attribution to Magical Panda Software, LLC and (optionally) link to the MagicalRecord open source repository - - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - -## Support - -MagicalRecord is provided free of charge. For Support, you have a few choices: - - -* Ask your support question on [Stackoverflow.com](http://stackoverflow.com), and tag your question with **MagicalRecord**. The core team will be notified of your question only if you mark your question with this tag. The general Stack Overflow community is provided the opportunity to answer the question to help you faster, and to reap the reputation points. If the community is unable to answer, we'll try to step in and answer your question. -* If you believe you have found a bug in MagicalRecord, please submit a support ticket on the [Github Issues page for MagicalRecord](http://github.com/magicalpanda/magicalrecord/issues). We'll get to them as soon as we can. Please do **NOT** as general questions on the issue tracker. All questions will be immediately closed and unanswered. -* For more personal or immediate support, [MagicalPanda](http://magicalpanda.com) is available for hire to consult on your project. - - -## Twitter - -Follow [@MagicalRecord](http://twitter.com/magicalrecord) on twitter to stay up to date on twitter with the latest updates to MagicalRecord. - diff --git a/Docs/Working-with-Managed-Object-Contexts.md b/Docs/Working-with-Managed-Object-Contexts.md new file mode 100644 index 000000000..8db3615e1 --- /dev/null +++ b/Docs/Working-with-Managed-Object-Contexts.md @@ -0,0 +1,85 @@ +# Working with Managed Object Contexts + +## Creating New Contexts + +A variety of simple class methods are provided to help you create new contexts: + +- `+ [NSManagedObjectContext MR_newContext]`: Sets the default context as it's parent context. Has a concurrency type of **NSPrivateQueueConcurrencyType**. +- `+ [NSManagedObjectContext MR_newMainQueueContext]`: Has a concurrency type of ** NSMainQueueConcurrencyType**. +- `+ [NSManagedObjectContext MR_newPrivateQueueContext]`: Has a concurrency type of **NSPrivateQueueConcurrencyType**. +- `+ [NSManagedObjectContext MR_newContextWithParent:…]`: Allows you to specify the parent context that will be set. Has a concurrency type of **NSPrivateQueueConcurrencyType**. +- `+ [NSManagedObjectContext MR_newContextWithStoreCoordinator:…]`: Allows you to specify the persistent store coordinator for the new context. Has a concurrency type of **NSPrivateQueueConcurrencyType**. + +## The Default Context + +When working with Core Data, you will regularly deal with two main objects: `NSManagedObject` and `NSManagedObjectContext`. + +MagicalRecord provides a simple class method to retrieve a default `NSManagedObjectContext` that can be used throughout your app. This context operates on the main thread, and is great for simple, single-threaded apps. + +To access the default context, call: + +```objective-c +NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext]; +``` + +This context will be used throughout MagicalRecord in any method that uses a context, but does not provde a specific managed object context parameter. + +If you need to create a new managed object context for use in non-main threads, use the following method: + +```objective-c +NSManagedObjectContext *myNewContext = [NSManagedObjectContext MR_newContext]; +``` + +This will create a new managed object context which has the same object model and persistent store as the default context, but is safe for use on another thread. It automatically sets the default context as it's parent context. + +If you'd like to make your `myNewContext` instance the default for all fetch requests, use the following class method: + +```objective-c +[NSManagedObjectContext MR_setDefaultContext:myNewContext]; +``` + +> **NOTE:** It is *highly* recommended that the default context is created and set on the main thread using a managed object context with a concurrency type of `NSMainQueueConcurrencyType`. + + +## Performing Work on Background Threads + +MagicalRecord provides methods to set up and work with contexts for use in background threads. The background saving operations are inspired by the UIView animation block methods, with a few minor differences: + +* The block in which you make changes to your entities will never be executed on the main thread. +* A single **NSManagedObjectContext** is provided for you within these blocks. + +For example, if we have Person entity, and we need to set the firstName and lastName fields, this is how you would use MagicalRecord to setup a background context for your use: + +```objective-c +Person *person = ...; + +[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ + + Person *localPerson = [person MR_inContext:localContext]; + localPerson.firstName = @"John"; + localPerson.lastName = @"Appleseed"; + +}]; +``` + +In this method, the specified block provides you with the proper context in which to perform your operations, you don't need to worry about setting up the context so that it tells the Default Context that it's done, and should update because changes were performed on another thread. + +To perform an action after this save block is completed, you can fill in a completion block: + +```objective-c +Person *person = ...; + +[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ + + Person *localPerson = [person MR_inContext:localContext]; + localPerson.firstName = @"John"; + localPerson.lastName = @"Appleseed"; + +} completion:^(BOOL success, NSError *error) { + + self.everyoneInTheDepartment = [Person findAll]; + +}]; +``` + +This completion block is called on the main thread (queue), so this is also safe for triggering UI updates. diff --git a/README.md b/README.md index 5503e55a5..1f27de79b 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,29 @@ MagicalRecord was inspired by the ease of Ruby on Rails' Active Record fetching. * Allow for clear, simple, one-line fetches * Still allow the modification of the NSFetchRequest when request optimizations are needed -#### Documentation - -* [Installation](Docs/Installation.md) -* [Getting Started](Docs/GettingStarted.md) -* [Fetching](Docs/Fetching.md) -* [Importing](Docs/Importing.md) -* [Logging](Docs/Logging.md) -* [Resources](Docs/Resources.md) -* [Saving](Docs/Saving.md) -* [Support](Docs/Support.md) -* [Threads](Docs/Threads.md) -* [iCloud](Docs/iCloud.md) -* [Default NSManagedObjectContext](Docs/DefaultManagedObjectContext.md) +## Documentation + +- [Installation](Docs/Installing-MagicalRecord.md) +- [Getting Started](Docs/Getting-Started.md) +- [Working with Managed Object Contexts](Docs/Working-with-Managed-Object-Contexts.md) +- [Creating Entities](Docs/Creating-Entities.md) +- [Deleting Entities](Docs/Deleting-Entities.md) +- [Fetching Entities](Docs/Fetching-Entities.md) +- [Saving Entities](Docs/Saving-Entities.md) +- [Usage Patterns](Docs/Usage-Patterns.md) +- [Importing Data](Docs/Importing-Data.md) +- [Logging](Docs/Logging.md) +* [Other Resources](Docs/Other-Resources.md) + +## Support + +MagicalRecord is provided as-is, free of charge. For support, you have a few choices: + +- Ask your support question on [Stackoverflow.com](http://stackoverflow.com), and tag your question with **MagicalRecord**. The core team will be notified of your question only if you mark your question with this tag. The general Stack Overflow community is provided the opportunity to answer the question to help you faster, and to reap the reputation points. If the community is unable to answer, we'll try to step in and answer your question. +- If you believe you have found a bug in MagicalRecord, please submit a support ticket on the [Github Issues page for MagicalRecord](http://github.com/magicalpanda/magicalrecord/issues). We'll get to them as soon as we can. Please do **NOT** ask general questions on the issue tracker. Support questions will be closed unanswered. +- For more personal or immediate support, [MagicalPanda](http://magicalpanda.com/) is available for hire to consult on your project. + + +## Twitter + +Follow [@MagicalRecord](http://twitter.com/magicalrecord) on twitter to stay up to date with the latest updates relating to MagicalRecord.