Like the title says how does one go about accessing an NSManagedObject that has been created in one block and then needs to be accessed in the other. I have the following implementation and was wondering if it's correct.
__block Person *newPerson;
@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
newPerson = [Person MR_createInContext:localContext];
newPerson.name = @"Bob";
} completion:^(BOOL success, NSError *error) {
@strongify(self);
// Called on main thread
PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:newPerson];
[self.navigationController pushViewController:personVC animated:YES];
}];
Am I correct in not needing to access newPerson from a localContext in the completion handler because it'll be executed on the main thread?
EDIT
It looks like the following is the proposed way:
__block NSManagedObjectID *newPersonObjectID;
@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Person *newPerson = [Person MR_createInContext:localContext];
newPerson.name = @"Bob";
newPersonObjectID = newPerson.objectID;
} completion:^(BOOL success, NSError *error) {
@strongify(self);
// Called on main thread
Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];
PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
[self.navigationController pushViewController:personVC animated:YES];
}];
Solution
This answer and comments lead to the following solution. A TemporaryID is being assigned to the object whilst it's being saved and therefore when trying to fetch the object with the TempID an exception occurs.
Rather than creating a whole new fetch request what can be done is asking the context to obtain the permanent IDs early and than acquiring the permanent ID of the object. For example:
__block NSManagedObjectID *newPersonObjectID;
@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Person *newPerson = [Person MR_createInContext:localContext];
newPerson.name = @"Bob";
[localContext obtainPermanentIDsForObjects:@[newPerson] error:NULL];
newPersonObjectID = newPerson.objectID;
} completion:^(BOOL success, NSError *error) {
@strongify(self);
// Called on main thread
Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];
PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
[self.navigationController pushViewController:personVC animated:YES];
}];
You can't directly pass managed objects between contexts. Each NSManagedObject
can only be accessed by its own context.
You'll need to pass its objectID
to the completion block, then have the main context fetch the object by calling one of the following methods:
-(NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID
This will create a fault to an object with the specified objectID, whether or not it actually exists in the store. If it doesn't exist, anything that fires the fault will fail with an exception.
-(NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID
error:(NSError **)error
This will fetch the object from the store that has that ID, or return nil if it doesn't exist. Unlike objectWithID
, the object won't be faulted; all its attributes will have been retrieved.
In either case, your local context must have saved the Person
object to the store for the main context to be able to fetch it.
More details about objectID
can be found in the Core Data Programming Guide
Edit by User Asking Question
This answer and comments lead to the correct solution. A TemporaryID is being assigned to the object whilst it's being saved and therefore when trying to fetch the object with the TempID an exception occurs.
Rather than creating a whole new fetch request what can be done is asking the context to obtain the permanent IDs early and than acquiring the permanent ID of the object. For example:
__block NSManagedObjectID *newPersonObjectID;
@weakify(self);
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Person *newPerson = [Person MR_createInContext:localContext];
newPerson.name = @"Bob";
[localContext obtainPermanentIDsForObjects:@[newPerson] error:NULL];
newPersonObjectID = newPerson.objectID;
} completion:^(BOOL success, NSError *error) {
@strongify(self);
// Called on main thread
Person *savedPerson = [[NSManagedObjectContext MR_defaultContext] objectWithID:newPersonObjectID];
PersonViewController *personVC = [[PersonViewController alloc] initWithPerson:savedPerson];
[self.navigationController pushViewController:personVC animated:YES];
}];