Here's what I'd do:
(1) the User class ought to be about the user's relationship with the app, a place for data that's just between the user and the app.
(2) For data that users want to share, have a Persona class that has an image, nickname, etc. Persona should contain a pointer to User and vice versa.
(3) Personae (Personas in common usage) make friend invitations and become friends.
(4) arrays of string object ids = bad, arrays of pointers = good. In fact, I can't think of a circumstance where I'd prefer a string object id over a pointer.
(5) A FriendInvitation ought to be its own object, where inviter and invitee are pointers to Persona.
(6) A friendship is a bilateral and symmetrical relationship (at least we always hope they are). A good representation for that might be a Friendship class that has an array of pointers to exactly two Persona objects.
Here are a few functions, given a data model:
Persona has a pointer to User, call it 'user', and User has a persona pointer. FriendInvitation has an inviter and invitee, both pointers to Persona. Friendship has an array of two pointers to Persona, call it friends
// get the current user's FriendInvitations
- (void)friendInvitationsWithCompletion:(void (^)(NSArray *, NSError *))completion {
PFObject *persona = [PFUser currentUser][@"persona"];
PFQuery *query = [PFQuery queryWithClassName:@"FriendInvitation"];
[query whereKey:@"invitee" equalTo:persona];
[query includeKey:@"inviter"];
[query findObjectsInBackgroundWithBlock:completion];
}
// get the current user's friendships
// remember, these are not the friends, but the objects that record pairings of friends.
// see the next function for the friends
- (void)friendshipsWithCompletion:(void (^)(NSArray *, NSError *))completion {
PFObject *persona = [PFUser currentUser][@"persona"];
PFQuery *query = [PFQuery queryWithClassName:@"Friendship"];
[query whereKey:@"friends" equalTo:persona];
[query includeKey:@"friends"];
[query findObjectsInBackgroundWithBlock:completion];
}
// get the current user's friends' personae
- (void)friendsWithCompletion:(void (^)(NSArray *, NSError *))completion {
PFObject *persona = [PFUser currentUser][@"persona"];
[self friendshipsWithCompletion:^(NSArray *friendships, NSError *error) {
if (!error) {
NSMutableArray *result = [@[] mutableCopy];
for (PFObject *friendship in friendships) {
NSArray *friends = friendship[@"friends"];
NSInteger indexOfFriend = ([friends indexOfObject:persona] == 0)? 1 : 0;
[result addObject:friends[indexOfFriend]];
}
completion(result, nil);
} else {
completion(nil, error);
}
}];
}
// agree to be friends with someone
- (void)becomeFriendsWith:(PFObject *)friend completion:(void (^)(BOOL, NSError *))completion {
PFObject *persona = [PFUser currentUser][@"persona"];
PFObject *friendship = [PFObject objectWithClassName:@"Friendship"];
friendship[@"friends"] = @[ persona, friend ];
[friendship saveInBackgroundWithBlock:completion];
}
// we could go on, but this should convey the basic ideas