title: Core Data saving in multiple threads
body: <p>I'm little bit confused about Core Data multithreading saving.</p>
  
  <p>I have following <code>NSManagedObjectContext</code> setup (same as <code>MagicalRecord</code>):</p>
  
  <pre><code>SavingContext (NSPrivateQueueConcurrencyType) has child DefaultContext(NSMainQueueConcurrencyType)
  </code></pre>
  
  <p>Each saving thread has own context (<code>NSPrivateQueueConcurrencyType</code>) with <code>DefaultContext</code> as parent.</p>
  
  <p><strong>So the question is</strong>: how can I rely on saving same type on different threads if I need to guarantee uniqueness?</p>
  
  <p>Here is small test example (Test is subclass of NSManagedObject):</p>
  
  <pre><code>@implementation Test
  
  + (instancetype) testWithValue:(NSString *) str {
      [NSThread sleepForTimeInterval:3];
      Test *t = [Test MR_findFirstByAttribute:@"uniqueField" withValue:str];
  
      if (!t) {
          NSLog(@"No test found!");
          t = [Test MR_createEntity];
      }
  
      t.uniqueField = str;
  
      return t;
  }
  @end
  </code></pre>
  
  <p>It first checks if there is a <code>Test</code> in newly created thread context (which has parent <code>DefaultContext</code>) and if no - create it in current thread context.</p>
  
  <p>And here is the test code:</p>
  
  <pre><code>NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  queue.maxConcurrentOperationCount = 2;
  
  [queue addOperationWithBlock:^{
      [Test operationWithValue:@"1"];
      [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait];
  }];
  
  [queue addOperationWithBlock:^{
      [Test operationWithValue:@"1"];
      [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait];
  }];
  
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      NSLog(@"Total tests: %lu", (unsigned long)[Test MR_countOfEntities]);
      [Test MR_truncateAll];
      [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
  });
  </code></pre>
  
  <p>It just run two operations, and trying to save same data. After creating Test I save all contexts (current thread, default context and root saving context). Most of the time there will be 2 tests. You can modify and add semaphore to ensure both threads reach checking at the same time.</p>
  
  <p><strong>Update 20.09.2014</strong>
  I've added the code, provided by mitrenegade (see below) and now my <code>Test.m</code> has a function:</p>
  
  <pre><code>-(BOOL)validateUniqueField:(id *)ioValue error:(NSError **)outError {
  
      // The property being validated must not already exist
  
      NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([self class])];
      fetchRequest.predicate = [NSPredicate predicateWithFormat:@"uniqueField == %@ AND self != %@", *ioValue, self];
  
      int count = [self.managedObjectContext countForFetchRequest:fetchRequest error:nil];
      if (count &gt; 0) {
          NSLog(@"Thread: %@ (isMain: %hhd), Validation failed!", [NSThread currentThread], [NSThread isMainThread]);
          return NO;
      }
  
      NSLog(@"Thread: %@ (isMain: %hhd), Validation succeeded!", [NSThread currentThread], [NSThread isMainThread]);
      return YES;
  }
  </code></pre>
  
  <p>With two threads creating the same value (test sample is in the beginning of post) I have the following output:</p>
  
  <pre><code>2014-09-20 11:48:53.824 coreDataTest[892:289814] Thread: &lt;NSThread: 0x15d38940&gt;{number = 3, name = (null)} (isMain: 0), Validation succeeded!
  2014-09-20 11:48:53.826 coreDataTest[892:289815] Thread: &lt;NSThread: 0x15e434a0&gt;{number = 2, name = (null)} (isMain: 0), Validation succeeded!
  2014-09-20 11:48:53.830 coreDataTest[892:289750] Thread: &lt;NSThread: 0x15e172c0&gt;{number = 1, name = main} (isMain: 1), Validation failed!
  2014-09-20 11:48:53.833 coreDataTest[892:289750] Thread: &lt;NSThread: 0x15e172c0&gt;{number = 1, name = main} (isMain: 1), Validation failed!
  2014-09-20 11:48:53.837 coreDataTest[892:289750] Thread: &lt;NSThread: 0x15e172c0&gt;{number = 1, name = main} (isMain: 1), Validation failed!
  2014-09-20 11:48:53.839 coreDataTest[892:289750] Thread: &lt;NSThread: 0x15e172c0&gt;{number = 1, name = main} (isMain: 1), Validation failed!
  2014-09-20 11:48:56.251 coreDataTest[892:289750] Total tests: 2
  </code></pre>
  
  <p>But if I look at underlying sqlite file there is no records at all (that means they stuck in Main Context)</p>