Save in One Context and Read in Another Context #561

Closed
rience opened this Issue Sep 4, 2013 · 6 comments

Comments

Projects
None yet
3 participants

rience commented Sep 4, 2013

I've found this strange behaviour that I can't find explanation for. Basically I'm using two 'saveWithBlock' invocations. In the second one - I'm trying to find an object that was saved in the first block.

- (void)testCreatePostAndReadFromOtherContext {
    __block NSManagedObjectID *postID;

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        VSPost *existingPost = [VSPost createInContext:localContext];
        existingPost.body = @"Test";

        postID = existingPost.objectID;
    } completion:^(BOOL saveSuccess, NSError *error) {
        VSPost *post = (VSPost *) [[NSManagedObjectContext defaultContext] objectWithID:postID];

        STAssertNotNil(post, @"actual post is nil");
        STAssertEqualObjects(post.body, @"Test", @"body is different");

        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
            VSPost *post = (VSPost *) [localContext objectWithID:postID];

            STAssertNotNil(post, @"actual post is nil");
            STAssertEqualObjects(post.body, @"Test", @"body is different");
        } completion:^(BOOL success, NSError *error) {
            [self notify:SenAsyncTestCaseStatusSucceeded];
        }];
    }];

    [self waitForStatus:SenAsyncTestCaseStatusSucceeded timeout:15.0];
}

This test throws an exception when I'm trying to access properties of post in second 'saveWithBlock'.

Moreover - I see Core Data SQL statements inserting Post into SQLite before execution is starting completion block.

Below you can find what XCode says about that.

screen shot 2013-09-04 at 11 37 20

Thank you for any help.

rience commented Sep 4, 2013

I've observed that "postID" that I'm storing at the end of first "saveWithBlock" is temporary one. So I've changed this code to store post in "__block" variable. Now - objectID is not temporary but I'm still getting the same error.

Moreover - if I change "objectWithID" to "existingObjectWithID" - then it works !

- (void)testCreatePostAndReadFromOtherContext {
    __block VSPost *existingPost;

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        existingPost = [VSPost createInContext:localContext];
        existingPost.body = @"Test";
    } completion:^(BOOL saveSuccess, NSError *error) {
        VSPost *post = (VSPost *) [[NSManagedObjectContext defaultContext] objectWithID:existingPost.objectID];

        STAssertNotNil(post, @"actual post is nil");
        STAssertEqualObjects(post.body, @"Test", @"body is different");

        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
            VSPost *post = (VSPost *) [localContext objectWithID:existingPost.objectID];

            STAssertNotNil(post, @"actual post is nil");
            STAssertEqualObjects(post.body, @"Test", @"body is different");
        } completion:^(BOOL success, NSError *error) {
            [self notify:SenAsyncTestCaseStatusSucceeded];
        }];
    }];

    [self waitForStatus:SenAsyncTestCaseStatusSucceeded timeout:15.0];
}
Owner

casademora commented Sep 4, 2013

That looks complicated. Why not try something like'

NSManagedObjectContext *context = [NSmanagedObjectContext MR_confinementContext];
VSPost *post = [VSPost MR_createInContext:context];
post.body = @"Test";
[context MR_saveToPersistentStoreAndWait];

This should make things synchronous for you.

Also, on the experimental branch, there is a method on NSManagedObject that can help you:

-[NSManagedObject MR_inContext:]

This does a better job of transferring an object from one context to another.

And, you can also check out

-[NSManagedObject MR_obtainPermanentObjectID]

which is a simple wrapper around the context method of a similar name.

On Sep 4, 2013, at 5:48 AM, Krzysztof Adamski notifications@github.com wrote:

I've observed that "postID" that I'm storing at the end of first "saveWithBlock" is temporary one. So I've changed this code to store post in "__block" variable. Now - objectID is not temporary but I'm still getting the same error.

Moreover - if I change "objectWithID" to "existingObjectWithID" - then it works !

  • (void)testCreatePostAndReadFromOtherContext {
    __block VSPost *existingPost;

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
    existingPost = [VSPost createInContext:localContext];
    existingPost.body = @"Test";
    } completion:^(BOOL saveSuccess, NSError *error) {
    VSPost *post = (VSPost *) [[NSManagedObjectContext defaultContext] objectWithID:existingPost.objectID];

    STAssertNotNil(post, @"actual post is nil");
    STAssertEqualObjects(post.body, @"Test", @"body is different");
    
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
        VSPost *post = (VSPost *) [localContext objectWithID:existingPost.objectID];
    
        STAssertNotNil(post, @"actual post is nil");
        STAssertEqualObjects(post.body, @"Test", @"body is different");
    } completion:^(BOOL success, NSError *error) {
        [self notify:SenAsyncTestCaseStatusSucceeded];
    }];
    

    }];

    [self waitForStatus:SenAsyncTestCaseStatusSucceeded timeout:15.0];
    }

    Reply to this email directly or view it on GitHub.

rience commented Sep 4, 2013

It's because I wanted things to save in background thread - not blocking the main thread. Hence using saveWithBackground. Otherwise - I'd block main thread with "saveToPersistentStoreAndWait".

Also, on the experimental branch, there is a method on NSManagedObject that can help you:
-[NSManagedObject MR_inContext:]

This works - since it's just simply doing "existingObjectWithID". But I try to understand when should I use objectWithID and when existingObjectWithID.

Owner

casademora commented Sep 4, 2013

Your unit tests are merely testing that the MagicalRecord wrappers work asynchronously. You're not testing your own API there. This is why I suggest an easier approach.

If you use a framework like kiwi or specta, then you might have some luck using their test spin waiting mechanisms as they are built in...

The MR_inContext: method shows you the general rules of thumb I've determined as to when you should use which of those framework methods, and uses them at the proper time.

On Sep 4, 2013, at 8:30 AM, Krzysztof Adamski notifications@github.com wrote:

It's because I wanted things to save in background thread - not blocking the main thread. Hence using saveWithBackground. Otherwise - I'd block main thread with "saveToPersistentStoreAndWait".

Also, on the experimental branch, there is a method on NSManagedObject that can help you:
-[NSManagedObject MR_inContext:]

This works - since it's just simply doing "existingObjectWithID". But I try to understand when should I use objectWithID and when existingObjectWithID.


Reply to this email directly or view it on GitHub.

rience commented Sep 4, 2013

I've used unit test here - but I've written unit test because similar code is failing on a real application code. Sorry for the confusion.

Thank's for 'inContext' method - I wasn't aware of it. I created my own category methods earlier.

Contributor

tonyarnold commented Apr 8, 2014

Given the age of this issue, and the volume of issues we have to work through, I've decided to close this alongside a number of other older issues.

If you can still replicate the issue under the latest in-development version of MagicalRecord (3.0 at the time of writing), please feel free to re-open and one of @magicalpanda/team-magicalrecord will take another look. Thanks!

tonyarnold closed this Apr 8, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment