Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

Adding To The self Destructing Message App

Is there any way to display the sender name with a different icon indicating that the file has been opened in the table view cell after the message has been opened the table view cell would show the sender name and new icon but nothing will happen when clicked on

11 Answers

Ben Jakuben
STAFF
Ben Jakuben
Treehouse Teacher

That's a cool idea. As of right now, the message is deleted from Parse.com, so there is no way to show it. You could save the message information on Parse.com or locally on the device.

If you save the message on Parse.com, perhaps you could add a new field called something like "recipientIdsRead". Then instead of deleting the message, you could move the recipientID for the user from the recipientIds field to this new field. When loading the inbox, you'd have to perform a separate query to search this field, and if a message is found, show it but disable it. You could break this up into small tasks - first make sure the ID gets copied over correctly instead of deleted, then add the query to the inbox and make sure it shows up, and then disable the item for tapping.

Alternatively, you could store the message within the app and display it as a read-only entry. In this case you wouldn't modify the backend at all, but once it was tapped move it into local storage or memory so that you can retrieve it and display it as read-only.

It's a bit of work, but a cool idea, so hopefully you try it out because I think you would learn a lot. :)

How would I move the recipientId to recipientIdRead?

How would I move the recipientId to recipientIdRead?

Ben Jakuben
STAFF
Ben Jakuben
Treehouse Teacher

What makes this easy is that Parse.com is super dynamic. It will handle pretty much whatever you throw at it. Check out this (untested) code:

NSMutableArray *recipientIds = [NSMutableArray arrayWithArray:[self.selectedMessage objectForKey:@"recipientIds"]];
NSMutableArray *recipientIdsRead = [NSMutableArray arrayWithArray:[self.selectedMessage objectForKey:@"recipientIdsRead"]];

[recipientIds removeObject:[[PFUser currentUser] objectId]];
[recipientIdsRead addObject:[[PFUser currentUser] objectId]];

[self.selectedMessage setObject:recipientIds forKey:@"recipientIds"];
[self.selectedMessage setObject:recipientIdsRead forKey:@"recipientIdsRead"];
[self.selectedMessage saveInBackground];

Use something like this instead of the part of didSelectRowAtIndexPath that currently just removes the ID or deletes the message. "recipientIdsRead" won't exist when you first run this, but Parse will just add a new field for you. Then when you do setObject:recipientIdsRead forKey:@"recipientIdsRead" it will store the new array in that field.

The other piece of this is that you need to update retrieveNewMessages to also query for rows where the objectId is in this new field. Check out this (again, untested) code:

PFQuery *query = [PFQuery queryWithClassName:@"Messages"];
[query whereKey:@"recipientIds" equalTo:[[PFUser currentUser] objectId]];

PFQuery *queryRead = [PFQuery queryWithClassName:@"Messages"];
[query whereKey:@"recipientIdsRead" equalTo:[[PFUser currentUser] objectId]];

PFQuery *queryTogether = [PFQuery orQueryWithSubqueries:@[query, queryRead]];
[queryTogether orderByDescending:@"createdAt"];
[queryTogether findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
...

That should get you all the messages, both new and read. The final piece is to disable the "read" messages. So in cellForRowAtIndexPath, you'd need to maybe indicate that the message was read, and then in didSelectRowAtIndexPath you'd need to skip all that code for these "read" messages.

Hope this helps! Again, it'll take a bit of work to get this correct, but it's a great idea!

Ok so I used what you showed me and the recipientIds have been successfully moved into recipientsIdsRead on parse the recipientIds field is empty and the recipientsIdsRead has all of the ids I'm not sure what to do from here though

Ben Jakuben
STAFF
Ben Jakuben
Treehouse Teacher

Sounds like you're on the right track! At this point you need to update the Inbox to also look for messages based on recipientIdsRead and not just recipientIds. The 2nd half of my previous post (with query, queryRead, and queryTogether) should give you a starting point on how to get the messages, but post back if you have trouble with them.

What does the queryTogether do?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

'query' is the regular one we are using that checks for new messages.

'queryRead' is a new one that checks for messages that have already been read.

'queryTogether' joins the two together to get one set of results. The results can then be displayed in the list. In cellForRowAtIndexPath you'll need to check each result to see if it's new or already read and then handle the table view cell accordingly.

How would you determine whether or not the user has opened the message. I tried this but it didn't work...

if ([self.selectedMessage objectForKey:@"recipientsIdsRead"] == [[PFUser currentuser] objectId]) {}

Am I close?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Looks close! First, there might be a typo: you have "recipientsIdsRead", which has an extra "s" compared to what we were using before.

Then, [self.selectedMessage objectForKey:@"recipientIdsRead"] should give you an NSArray of object ID strings. So you would need to check if the array contains [[PFUser currentUser] objectId] instead of the straight equality check that you have (because the array won't equal a single string ID).

I made that typo a long time ago so thats what I've been using the whole project but what do you mean by "check if the array contains [[PFUser currentUser] objectId] instead of the straight equality check that you have"?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

The code you have there is trying to compare a string value with an array variable. objectForKey:@"recipeintsIdsRead" gives you a whole array.

So instead, you'll need to check each of the items in the array. There is a containsObject method that might work, but if not, you could loop through the array like we do for the isFriends method in EditFriendsViewController:

- (BOOL)isFriend:(PFUser *)user {
    for(PFUser *friend in self.friends) {
        if ([friend.objectId isEqualToString:user.objectId]) {
            return YES;
        }
    }

    return NO;
}

So like this

- (BOOL)isRead:(PFUser *)user {
    for(PFUser *idsRead in self.idsRead) {
        if ([idsRead.objectId isEqualToString:user.objectId]) {
            return YES;
        }
    }

    return NO;
}
Ben Jakuben
Ben Jakuben
Treehouse Teacher

That looks like it would work! I would change the name "idsRead" for your PFUser variable to something more meaningful, though, like "aUser" or "readUser".

I used the containsObject method instead and it kind of works but its pretty glitchy is there any way I can send you the whole project?

Ben Jakuben
Ben Jakuben
Treehouse Teacher

Sure thing! I can't promise a quick turnaround for help above and beyond the course material, but I'd be happy to take a look. :) Email it to help@teamtreehouse.com and ask that it be forwarded to me.

Ok I sent it