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 Build a Self-Destructing Message iPhone App Capturing Photo and Video Using UIImagePickerController Uploading the File and Message

robert cioffi
robert cioffi
3,466 Points

libc++abi.dylib: terminating with uncaught exception of type NSException

I think with the newer version of iOS or Xcode there is a bug, because I've typed everything as in the video but sometimes I get this exception, I did some debugging and found that the line that causes it is:
[message setObject:self.recipients forKey:@"recipientIds"]; Otherwise without that key identifier it works fine. anybody having the same issue? anyone know how to fix it? thanks

3 Answers

Stone Preston
Stone Preston
42,016 Points

can you post more information about the exception. it should provide some more information about it in the console

robert cioffi
robert cioffi
3,466 Points

Ok, the specific error is: 2014-08-22 13:12:28.185 ribbit[1050:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't use nil for keys or values on PFObject. Use NSNull for values.' *** First throw call stack: (0x2e7e0f03 0x38f75ce7 0x2e7e0e45 0x10f21d 0x10f3eb 0x10046b 0x14f1d7 0x14efc1 0x3945ed53 0x3945ed3f 0x394616c3 0x2e7ab679 0x2e7a9f45 0x2e7147a9 0x2e71458b 0x336816d3 0x31073891 0x10186d 0x39473ab7) libc++abi.dylib: terminating with uncaught exception of type NSException

the real question is why is it a nil key?

and I did what Ben J said to do on someone else's post about a similar issue which is do the NSLog: NSLog(@"%@", self.friends); and it outputs: 2014-08-22 13:11:18.297 ribbit[1050:60b] ( "<PFUser:SN4AVKxV1J:(null)> {\n email = \"dfd@dfd.com\";\n friendsRelation = \"<PFRelation: 0x16592db0>(<00000000>.(null) -> _User)\";\n username = bob;\n}"

SO it appears there is a null in that output, I'm not sure what to fix because if I take message[@"recipientsIds"] = self.recipients; out of the CameraViewController, the image uploads to Parse.com just fine, but then it doesn't have associated recipients listed. This means self.recipients isn't being loaded somewhere else in the file, but where?

Complete CameraViewController Code:
//
//  CameraViewController.m
//  ribbit
//
//  Created by Robert Cioffi on 7/10/14.
//  Copyright (c) 2014 Cioffi. All rights reserved. 
//

#import "CameraViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>

@interface CameraViewController ()

@end

@implementation CameraViewController


- (void)viewDidLoad
{
[super viewDidLoad];
self.friendsRelation = [[PFUser currentUser] objectForKey:@"friendsRelation"];  //current user relations to friends, but no friends have been "Grabbed" yet

self.imagePicker = [[UIImagePickerController alloc] init];
self.imagePicker.delegate = self;
self.imagePicker.allowsEditing = NO;

if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
    self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} // if  camera available
else {
    self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
} // if no camera available like in simulator

self.imagePicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:self.imagePicker.sourceType];

[self presentViewController:self.imagePicker animated:NO completion:nil];
}

-(void) viewWillAppear:(BOOL)animated   {  //this will appear is where loaded items loop back to normally
[super viewWillAppear:animated];

//same code as friendsTableViewController, pulls friends list
PFQuery *query = [self.friendsRelation query];  //grabs relations to current user
[query orderByAscending:@"username"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if(error){
        NSLog(@"Error: %@ %@", error, [error userInfo]);
    }
    else {
        self.friends = objects;
        NSLog(@"%@", self.friends);
        [self.tableView reloadData];
    }

}];  //end of findObjectsInBackgroundWithBlock

//same code as above but when click camera 2nd time it works, because view is ALREADY Loaded first time its used
if (self.image == nil && [self.videoFilePath length] != 0){
    self.imagePicker = [[UIImagePickerController alloc] init];
    self.imagePicker.delegate = self;
    self.imagePicker.allowsEditing = NO;
    self.imagePicker.videoMaximumDuration = 10;  //set duration of video to 10secs

    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    } // if  camera available
    else {
        self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    } // if no camera available like in simulator

    self.imagePicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:self.imagePicker.sourceType];

    [self presentViewController:self.imagePicker animated:NO completion:nil];
}
}
#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.friends count];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

PFUser *user = [self.friends objectAtIndex:indexPath.row];
cell.textLabel.text = user.username;

if ([self.recipients containsObject:user.objectId]) {
    cell.accessoryType = UITableViewCellAccessoryNone;
} else {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}

return cell;
}

#pragma mark - Image Picker Controller Delegate

-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker    {
[self dismissViewControllerAnimated:NO completion:nil];  // just needed in method to make "Cancel" button work, otherwise cancel doesn't do anything

[self.tabBarController setSelectedIndex:0]; //returns from camera view to other tab
}


-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
    //A photo was taken/selected
    self.image = [info objectForKey:UIImagePickerControllerOriginalImage];
    if (self.imagePicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
        //save the image!
        UIImageWriteToSavedPhotosAlbum(self.image, nil, nil, nil);
    }
    [self dismissViewControllerAnimated:YES completion:nil];
        //dismiss camera view
}
else {
        // A video was taken or selected
        self.videoFilePath = (NSString *)[[info objectForKey:UIImagePickerControllerMediaURL] path];
        if (self.imagePicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
        //save the video!
            if(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(self.videoFilePath)) {
            UISaveVideoAtPathToSavedPhotosAlbum(self.videoFilePath, nil, nil, nil);
        }
        }
    [self dismissViewControllerAnimated:YES completion:nil];  //close camera

    }

}

-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
PFUser *user = [self.friends objectAtIndex:indexPath.row];

if(cell.accessoryType == UITableViewCellAccessoryNone) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    [self.recipients addObject:user.objectId];
}
else {
    cell.accessoryType = UITableViewCellAccessoryNone;
    [self.recipients removeObject:user.objectId];
}

}

#pragma mark - IBActions
- (IBAction)send:(id)sender {

if(self.image == nil && self.videoFilePath.length == 0)
{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Try Again!" message:@"Please capture or select a photo or video to share!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alertView show];
    [self presentViewController:self.imagePicker animated:NO completion:nil];
} else {
    [self uploadMessage];
   // removed reset because of bug, put below
    [self.tabBarController setSelectedIndex:0]; //goes to inbox
}
}




- (IBAction)cancel:(id)sender {
[self reset];

[self.tabBarController setSelectedIndex:0]; //goes to inbox

}

#pragma mark - Helper Methods
- (void) uploadMessage {
NSData *fileData;
NSString *fileName;
NSString *fileType;

// check if image or video
if(self.image != nil) {
    UIImage *newImage = [self resizeImage:self.image toWidth:320.0f andHeight:480.0f];
    fileData = UIImagePNGRepresentation(newImage);
    fileName = @"image.png";
    fileType = @"image";

} else {
    fileData = [NSData dataWithContentsOfFile:self.videoFilePath];
    fileName = @"video.mov";
    fileType = @"video";
}

PFFile *file = [PFFile fileWithName:fileName data:fileData];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
    if(error) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"An error Occurred!" message:@"Please try sending your message again!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alertView show];
    } else {

        PFObject *message = [PFObject objectWithClassName:@"Messages"];
        message[@"file"] = file;
        message[@"fileType"] = fileType;
        //message[@"recipientsIds"] = self.recipients;
        NSLog(@"the receipients are: %@", self.recipients);  //NULL but why?
        message[@"senderId"] = [[PFUser currentUser]objectId];
        message[@"senderName"] = [[PFUser currentUser]username];

       [message saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
            if(error) {
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"An error Occurred!" message:@"Please try sending your message again!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                [alertView show];

            } else {
                    //everything was successful!
                 [self reset];
                }
        }];
   }
}];


// IF image, shrink it
// upload the file itself
// upload the message details
}

- (void)reset {
self.image = nil;
self.videoFilePath = nil;
[self.recipients removeAllObjects];
}

- (UIImage *) resizeImage:(UIImage *)image toWidth:(float)width andHeight:(float)height {

CGSize newSize = CGSizeMake(width, height);
CGRect newRectangle = CGRectMake(0, 0, width, height);
UIGraphicsBeginImageContext(newSize);
[self.image drawInRect:newRectangle];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return resizedImage;
}



@end

****This is why I originally gave up on this project a month ago, I want to figure this out and move on

robert cioffi
robert cioffi
3,466 Points

Anybody have the same problem or have a solution for my question above?

reardelt
reardelt
8,030 Points

HI Robert, I think I had the same issue. The self.recipient was null. I think the issue is when we set self.recipient =nil in the reset method. the reset method is called before we send the data off to parse.com

reardelt
reardelt
8,030 Points

so I guess don't remove all Objects from in reset method. remove all objects in the viewWillAppear method.

Stone Preston
Stone Preston
42,016 Points

looks like you implemented the incorrect tableViewDelegate method. you implemented didDeselectRowAtIndexPath:

-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
PFUser *user = [self.friends objectAtIndex:indexPath.row];

if(cell.accessoryType == UITableViewCellAccessoryNone) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    [self.recipients addObject:user.objectId];
}
else {
    cell.accessoryType = UITableViewCellAccessoryNone;
    [self.recipients removeObject:user.objectId];
}

}

but you should have implemented didSelectRowAtIndexPath. change the method name to didSelect:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
PFUser *user = [self.friends objectAtIndex:indexPath.row];

if(cell.accessoryType == UITableViewCellAccessoryNone) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    [self.recipients addObject:user.objectId];
}
else {
    cell.accessoryType = UITableViewCellAccessoryNone;
    [self.recipients removeObject:user.objectId];
}

}

see if that fixes things