saveWithBlock:completion: not saving #656

Closed
timothycosta opened this Issue Jan 31, 2014 · 6 comments

Projects

None yet

4 participants

@timothycosta

Hi, and thank you for your great library!

I'm having trouble getting my objects to save using the method saveWithBlock:completion. When I switch over to using normal blocks it works fine. Using version 2.2develop. The [NSObject execute:] methods just wrap dispatch_async.

Here's the code that works. I have commented out the saveWithBlock: parts. In the code below executeBlockOnMainThread: replaces completion:.

I'd be grateful for any help!

- (void)requestRelatedPhrasesForCard:(Card*)card completionDelegate:(id)delegate{
[card.managedObjectContext MR_saveToPersistentStoreAndWait]; // This is necessary because the fragment may otherwise get lost.

//  [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
[NSObject executeBlockInBackground:^{

    Card *localCard = [card MR_inContext:[NSManagedObjectContext MR_contextForCurrentThread]]; //MR_inContext:localContext];
    NSRange range = [localCard.fragment rangeOfString:localCard.term options:NSCaseInsensitiveSearch];
    if (range.location == NSNotFound){
        return; // Why would you set a fragment with no term in it?!
    }

    NSString *fragmentString = [localCard.fragment stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    NSString *url = [NSString stringWithFormat:@"https://www.lingq.com/api/languages/%@/phrases/?word=%@&frag=%@&start=%@", appDelegate.currentLanguage, localCard.term, fragmentString, @(range.location)];
    NSMutableURLRequest *request = [LQAPI requestForURL:url body:nil HTTPMethod:@"GET"];
    NSData *response = [LQAPI executeURLRequest:request];

    NSDictionary *responseDictionary = [LQAPI dictionaryFromJSONData:response];
    if (responseDictionary == nil){
        return; // There was no response or it was not a dictionary, meaning there are no phrases.
    }

    NSArray *phrases = [responseDictionary objectForKey:@"phrases"];
    NSMutableArray *cards = [NSMutableArray array];
    int index = 0;
    for (NSDictionary *d in phrases){
        Card *c = [self cardFromDictionary:d content_id:localCard.content_Id index:index type:BlueCard];
        c.parentID = localCard.mId; // This marks it as a phrase
        [cards addObject:c];
        index++;
    }
    [localCard.managedObjectContext MR_saveToPersistentStoreAndWait]; // This shouldn't be necessary using saveWithBlock: and it didn't help when I tried it.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parentID = %@", localCard.mId];
    NSArray *phrases2 = [Card MR_findAllWithPredicate:predicate]; // All the objects appear fine here
    [NSObject executeBlockOnMainThread:^{ // This would have been in the completion block below
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parentID = %@", card.mId];
        NSArray *phrases = [Card MR_findAllWithPredicate:predicate];
        [delegate didFetchPhrases:phrases forCard:card];
    }];
}];
/*   completion:^(BOOL success, NSError *error) {
     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parentID = %@", card.mId];
     NSArray *phrases = [Card MR_findAllWithPredicate:predicate]; // This doesn't find any objects
     [delegate didFetchPhrases:phrases forCard:card];
 }];*/
}
@tonyarnold
Contributor

Hi @arsenius — thanks for the kind words!

Card *localCard = [card MR_inContext:[NSManagedObjectContext MR_contextForCurrentThread]]; //MR_inContext:localContext];

Don't do that — MR_contextForCurrentThread is unreliable and deprecated. You had it right the first time:

Card *localCard = [card MR_inContext:localContext];

Also, everything in the block is executed before the save to disk occurs. Pushing things off using NSObject executeBlockOnMainThread: is highly unlikely to work (if it does, it won't work every time). Can you try reverting everything to how it should be — no calling save within the save block, always accessing/creating objects in localContext. And see what happens then?

@tonyarnold tonyarnold was assigned Jan 31, 2014
@timothycosta

Well, I don't know what was going on before, but it does work now. Honestly, I wrestled with that code for quite a while before posting it on here. All I did is revert all those parts I had commented out and it works fine now.

If you don't mind, could you explain about why executeBlockOnMainThread: is unlikely to work? I only just learned about saveWithBlock: a few days ago, and until then I had been using the method above quite a bit without major problems. It is quite convenient when I have fetched something from the server that I don't want to save via core data, but needs to reach the main thread.

Also, if MR_contextForCurrentThread is deprecated, is there a replacement for it? localContext obviously wasn't available with executeBlockInBackground:. What if I need to get a context in a background thread? Is it just always dangerous? I think I've got a few calls to that peppered throughout my code.

This works:
- (void)requestRelatedPhrasesForCard:(Card*)card completionDelegate:(id)delegate{
[card.managedObjectContext MR_saveToPersistentStoreAndWait]; // This is necessary because the fragment may otherwise get lost.

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {

    Card *localCard = [card MR_inContext:localContext];
    NSRange range = [localCard.fragment rangeOfString:localCard.term options:NSCaseInsensitiveSearch];
    if (range.location == NSNotFound){
        return; // Why would you set a fragment with no term in it?!
    }

    NSString *fragmentString = [localCard.fragment stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    NSString *url = [NSString stringWithFormat:@"https://www.lingq.com/api/languages/%@/phrases/?word=%@&frag=%@&start=%@", appDelegate.currentLanguage, localCard.term, fragmentString, @(range.location)];
    NSMutableURLRequest *request = [LQAPI requestForURL:url body:nil HTTPMethod:@"GET"];
    NSData *response = [LQAPI executeURLRequest:request];

    NSDictionary *responseDictionary = [LQAPI dictionaryFromJSONData:response];
    if (responseDictionary == nil){
        return; // There was no response or it was not a dictionary, meaning there are no phrases.
    }

    NSArray *phrases = [responseDictionary objectForKey:@"phrases"];
    NSMutableArray *cards = [NSMutableArray array];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parentID = %@", localCard.mId];
    [Card MR_deleteAllMatchingPredicate:predicate];
    int index = 0;
    for (NSDictionary *d in phrases){
        Card *c = [self cardFromDictionary:d content_id:localCard.content_Id index:index type:BlueCard];
        c.parentID = localCard.mId; // This marks it as a phrase
        c.fragment = localCard.fragment;
        c.mId = @(arc4random());
        [cards addObject:c];
        index++;
    }
    NSArray *phrases2 = [Card MR_findAllWithPredicate:predicate];
    ALog(@"phrases: %@", phrases2);
}
completion:^(BOOL success, NSError *error) {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parentID = %@", card.mId];
    NSArray *phrases = [Card MR_findAllWithPredicate:predicate];
    [delegate didFetchPhrases:phrases forCard:card];
}];

}

@timothycosta timothycosta reopened this Jan 31, 2014
@timothycosta

Well, I don't know what to say. Here I am a few hours later and that same code above is no longer working as before. All my phrases disappear by the time the completion block is called. I guess it has to be some side effect from some code someplace else in the app?

@casademora
Member

This really seems like a stack overflow question. Please post your question there with your code samples. We do monitor the [MagicalRecord] tag and try to answer usage problems there. This is a list for actual bugs with MagicalRecord.

Thanks

On Jan 31, 2014, at 9:38 AM, Timothy Costa notifications@github.com wrote:

Well, I don't know what to say. Here I am a few hours later and that same code above is no longer working as before. All my phrases disappear by the time the completion block is called. I guess it has to be some side effect from some code someplace else in the app?


Reply to this email directly or view it on GitHub.

@jaysonjh

I'm having trouble getting my objects to save using the method saveWithBlock:completion too.
when I call savewithBlock:completion, console is printing save finished & insert sql log.But ,when I call some 'fetch Method',there is empty in db.

my code like this:
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name= %@", @"test"];
[Person MR_deleteAllMatchingPredicate:predicate];
Person *p= [Person MR_createInContext:localContext];
p.name = @"sssss";
}
completion:^(BOOL success, NSError *error) {
if(!success){
NSLog(@"error:%@",error);
}
}];

@casademora
Member

Well, that should be working. Is there anything else in the setup you can provide for troubleshooting? Also please close this ticket and open a question on Stackoverflow.com and use the [MagicalRecord] tag. We actively monitor usage and troubleshooting questions there. You may also get a faster response by the greater stack overflow community. We want to use the Issues list for actual problems and bugs in MagicalRecord. Thanks for understanding.

@tonyarnold tonyarnold closed this Apr 8, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment