1
votes

Consider this simple method:

- (void)connectSpouseToCurrentUser:(PFUser *)spouse completionBlock:(void (^)(NSError *error))completionBlock {
    [PFUser currentUser][@"spouse"] = spouse;        
    [[PFUser currentUser] saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", [error userInfo]);
        }

        spouse[@"spouse"] = [PFUser currentUser];
        [spouse saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
           // This code never executes
            NSLog(@"Error: %@", error);
            self.spouse = spouse;
            if (completionBlock) {
                completionBlock(error);
            }
        }];
    }];
}

For some reasons my latest completion block is not being called.

Does anybody know why this is happening?

3
Have you set a breakpoint to see what is happening? - Paulw11
Sure, the execution doesn't stop on the breakpoint. - Andrey Gordeev
Where did you set the breakpoint? - Paulw11
NSLog(@"Error: %@", error);. - Andrey Gordeev
You should set it at spouse[@"spouse"] = [PFUser currentUser]; and verify that spouse isn't nil - Paulw11

3 Answers

1
votes

For people who are encountering this problem with one-to-many relationship using Array column type,

If you assign the array to the Array column type, then the completion block is not called. However, the data is saved. However, there is no way to visualize the connection in the parse.com dashboard.

I have two tables, User and Education. An User can have multiple Education objects. In the following code, the completion block is never called.

import Parse

var education : [MyEducation] = [MyEducation]()

let edu1 : MyEducation = MyEducation()
edu1["schoolName"] = "Stanford"
edu1["fieldOfStudy"] = "Engineering"
edu1["startDate"] = NSDate()
education.append(edu1)

let edu2 : MyEducation = MyEducation()
edu2["schoolName"] = "UCLA"
edu2["fieldOfStudy"] = "Data Science"
edu2["startDate"] = NSDate()
education.append(edu2)

PFUser.currentUser()?["education"] = education
PFUser.currentUser()?.saveInBackgroundWithBlock{(success: Bool, error: NSError?) -> Void in
        let a : Int = 1
}

However, if you code the following, the completion block IS called.

import Parse

var education : [MyEducation] = [MyEducation]()

let edu1 : MyEducation = MyEducation()
edu1["schoolName"] = "Stanford"
edu1["fieldOfStudy"] = "Engineering"
edu1["startDate"] = NSDate()
edu1["user"] = PFUser.currentUser()
education.append(edu1)

let edu2 : MyEducation = MyEducation()
edu2["schoolName"] = "UCLA"
edu2["fieldOfStudy"] = "Data Science"
edu2["startDate"] = NSDate()
edu2.["user"] = PFUser.currentUser()
education.append(edu2)

PFUser.currentUser()?.saveInBackgroundWithBlock{(success: Bool, error: NSError?) -> Void in
        let a : Int = 1
}

So in the end, which approach to choose is your call. The former makes visualization harder (or impossible?) in dashboard but you can use includeKey("education") later for PFUser queries.

In the latter approach you must query the Education class for full PFObject but the pointer to User from the Education class is visible in dashboard as well as completion block being called.

I think Parse did not design relation acquisition very well and this behavior is not documented.

0
votes

You should set your code inside a succeeded or !error block like this:

if (succeeded) { // like this
    spouse[@"spouse"] = [PFUser currentUser];
    [spouse saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
        if (!error) { // or like this
           // This code never executes
            NSLog(@"Error: %@", error);
            self.spouse = spouse;
            if (completionBlock) {
                completionBlock(error);
            }
        }
    }];
}
0
votes

I have the same problem and I found only one useful comment about this topic on parse helper forum. I gave it a try and it works. https://www.parse.com/questions/saveinbackgroundwithblock-not-being-called-on-1219

So the main problem is that you create a pointer from user to spouse and also from spouse to user. I know it's easier to work with both pointers.

In my case i have users and messages and all the messages had pointers to users and all the users have pointers to last messages. I removed the last one. Instead of a pointer I query the last message from parse when I need a fresh one and store it as a normal objective c proprerty.

PFQuery *messageQuery = [Message query];
[messageQuery whereKey:@"owner" equalTo:self.createdBy];
[messageQuery orderByDescending:@"createdAt"];
[messageQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
    if (object && !error)
    self.user.lastMessage = (Message *)object;
}];

Hope this will help you. Also I strongly advise you to user parse subclassing. It's a lot easier to code and to have a good sight on what you're doing.