Parse Relation [Error]: can't add a non-pointer to a relation (Code: 111, Version: 1.7.5)

773 views Asked by At

I have a jobs app that enables users to view jobs. Jobs is a class in my Parse backend. I want to create a Favorites tab where user can mark certain jobs.

I've created a Relations Column in my User Class referring it to my Jobs class.

However, I have ran into this when the user taps to make the job a favorite: [Error]: can't add a non-pointer to a relation (Code: 111, Version: 1.7.5)

I feel like my PFRelation coding is spot on. I've researched this error but cannot seem to find any subject that relates to my problem. I must be making a mistake somewhere but

@interface JobDetailViewController ()

@end

@implementation JobDetailViewController

@synthesize jobPhoto;
@synthesize RotationLabel;
@synthesize QualificationsTextView;
@synthesize Job_DescriptionTextView;
@synthesize TypeLabel;
@synthesize LocationLabel;
@synthesize ClearanceLabel;
@synthesize PositionLabel;
@synthesize job;
@synthesize POC;
@synthesize Email;
@synthesize Phone;
@synthesize Apply;



- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [_Scroller setScrollEnabled:YES];
    [_Scroller setContentSize:CGSizeMake(320, 2200)];

    [self dismissKeyboard];
    self.PositionLabel.text = job.position;
    self.RotationLabel.text = job.rotation;
    self.LocationLabel.text = job.location;
    self.TypeLabel.text = job.type;
    self.ClearanceLabel.text = job.clearance;
    jobPhoto.file = (PFFile *)job.imageFile;
    [jobPhoto loadInBackground];

    NSMutableString *pocText = [NSMutableString string];
    for (NSString* poc in job.poc) {
        [pocText appendFormat:@"%@\n", poc];
    }
    self.POC.text = pocText;

    NSMutableString *emailText = [NSMutableString string];
    for (NSString* email in job.email) {
        [emailText appendFormat:@"%@\n", email];
    }
    self.Email.text = emailText;


    NSMutableString *phoneText = [NSMutableString string];
    for (NSString* phone in job.phone) {
        [phoneText appendFormat:@"%@\n", phone];
    }
    self.Phone.text = phoneText;

    NSMutableString *applyText = [NSMutableString string];
    for (NSString* apply in job.apply) {
        [applyText appendFormat:@"%@\n", apply];
    }
    self.Apply.text = applyText;

    NSMutableString *qualificationsText = [NSMutableString string];
    for (NSString* qualifications in job.qualifications) {
        [qualificationsText appendFormat:@"%@\n", qualifications];
    }
        self.QualificationsTextView.text = qualificationsText;

    NSMutableString *job_descriptionText = [NSMutableString string];
    for (NSString* job_description in job.job_description) {
        [job_descriptionText appendFormat:@"%@\n", job_description];
    }
    self.Job_DescriptionTextView.text = job_descriptionText;
}



- (IBAction)favoriteButtonAction:(id)sender {
    PFObject *jobs = [PFObject objectWithClassName:@"Jobs"];
    PFUser *user = [PFUser currentUser];
    PFRelation *relation = [user relationForKey:@"Favorites"];
    [relation addObject:jobs];

    [user saveInBackground];



}


- (void)viewDidUnload
{
    [self setJobPhoto:nil];
    [self setPositionLabel:nil];
    [self setRotationLabel:nil];
    [self setLocationLabel:nil];
    [self setTypeLabel:nil];
    [self setQualificationsTextView:nil];
    [self setJob_DescriptionTextView:nil];
    [self setPOC: nil];
    [self setPhone:nil];
    [self setEmail:nil];
    [self setApply:nil];
    [self dismissKeyboard];

    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

-(void) dismissKeyboard {
    [Email resignFirstResponder];
    [POC resignFirstResponder];
    [Phone resignFirstResponder];
    [Job_DescriptionTextView resignFirstResponder];
    [QualificationsTextView resignFirstResponder];
}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
 return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void) favoriteSuccess {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success!" message:@"Added job to Favorites!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}


- (void) favoriteFailed {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Ooooops!" message:@"Error occurred while adding to Favorites!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}



@end

JobListViewController that populates the jobs prior to the JobDetailViewController:

#import "JobDetailViewController.h"
#import "JobListViewController.h"
#import "Job.h"
#import "SearchedResultCell.h"
#import <Parse/Parse.h>



@interface JobListViewController () <UISearchDisplayDelegate, UISearchBarDelegate> {

}

@property (nonatomic, weak) IBOutlet UISearchBar *searchedBar;
@property (nonatomic, strong) NSString *mainTitle;
@property (nonatomic, strong) NSString *subTitle;
@property (nonatomic, assign) BOOL canSearch;

@end

    @interface JobListViewController ()

    @end

    @implementation JobListViewController
    {}
    @synthesize searchedBar;
    @synthesize mainTitle;
    @synthesize subTitle;
    @synthesize canSearch;


    - (id)initWithCoder:(NSCoder *)aCoder
    {
        self = [super initWithCoder:aCoder];
        if (self) {
            // Custom the table

            // The className to query on
            self.parseClassName = @"Jobs";

            // The key of the PFObject to display in the label of the default cell style
            self.textKey = @"Position";

            // Whether the built-in pull-to-refresh is enabled
            self.pullToRefreshEnabled = YES;

            // Whether the built-in pagination is enabled
            self.paginationEnabled = YES;

            // The number of objects to show per page
            self.objectsPerPage = 20;
        }
        return self;
    }

    - (void)viewDidLoad
    {
        [super viewDidLoad];



    }

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];

        self.canSearch = 0;

    }

    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
    }

    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
    }

    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
    }

    - (void)viewDidUnload
    {
        [super viewDidUnload];
        // Release any retained subviews of the main view.
    }

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }

    - (void)objectsWillLoad {
        [super objectsWillLoad];

        // This method is called before a PFQuery is fired to get more objects
    }


    - (PFQuery *)queryForTable
    {
        PFQuery *query1 = [PFQuery queryWithClassName:@"Jobs"];
        NSString *searchThis = [searchedBar.text capitalizedString];
        [query1 whereKey:@"Position" containsString:searchThis];
        PFQuery *query2 = [PFQuery queryWithClassName:@"Jobs"];
        [query2 whereKey:@"Type" containsString:searchThis];
        PFQuery *query = [PFQuery orQueryWithSubqueries:@[query1,query2]];
            [query orderByDescending:@"createdAt"];

        if (self.pullToRefreshEnabled) {
            query.cachePolicy = kPFCachePolicyNetworkOnly;
        }

        // If no objects are loaded in memory, we look to the cache first to fill the table
        // and then subsequently do a query against the network.

        return query;
    }







    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object: (PFObject *)object
    {
        static NSString *simpleTableIdentifier = @"JobCell";
            static NSString *pimpleTableIdentifier = @"JobCell";

        UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];

        SearchedResultCell *bell = [self.tableView dequeueReusableCellWithIdentifier:pimpleTableIdentifier];

        if (bell == nil) {
            bell = [[SearchedResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:pimpleTableIdentifier];

            }

            [self configureSearchResult:bell atIndexPath:indexPath object:object];


        }

        // Configure the cell
        PFFile *thumbnail = [object objectForKey:@"imageFile"];
        PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
        thumbnailImageView.image = [UIImage imageNamed:@"AppIcon.png"];
        thumbnailImageView.file = thumbnail;
        [thumbnailImageView loadInBackground];

        UILabel *positionLabel = (UILabel*) [cell viewWithTag:101];
        positionLabel.text = [object objectForKey:@"Position"];
        UILabel *rotationLabel = (UILabel*) [cell viewWithTag:102];
        rotationLabel.text = [object objectForKey:@"Rotation"];
        UILabel *locationLabel = (UILabel*) [cell viewWithTag:103];
        locationLabel.text = [object objectForKey:@"Location"];
        UILabel *typeLabel = (UILabel*) [cell viewWithTag:104];
        typeLabel.text = [object objectForKey:@"Type"];


    return cell;
    }


    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.identifier isEqualToString:@"showJobDetail"]) {
            NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

            Job *job = [[Job alloc] init];

            JobDetailViewController *destViewController = segue.destinationViewController;

            PFObject *object = [self.objects objectAtIndex:indexPath.row];
            job.position = [object objectForKey:@"Position"];
            job.poc = [object objectForKey:@"POC"];
            job.email = [object objectForKey:@"Email"];
            job.phone = [object objectForKey:@"Phone"];
            job.apply = [object objectForKey:@"Apply"];
            job.imageFile = [object objectForKey:@"imageFile"];
            job.rotation = [object objectForKey:@"Rotation"];
            job.location = [object objectForKey:@"Location"];
              job.type = [object objectForKey:@"Type"];
            job.clearance = [object objectForKey:@"Clearance"];
            job.job_description = [object objectForKey:@"Job_Description"];
            job.qualifications = [object objectForKey:@"Qualifications"];
            destViewController.job = job;

        }

    }

    - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
    {

        [self clear];

        self.canSearch = 1;

        [self.searchedBar resignFirstResponder];

        [self queryForTable];
        [self loadObjects];

    }



    - (void)configureSearchResult:(SearchedResultCell *)cell atIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
    {
        mainTitle = [object objectForKey:@"Position"];
        cell.mainTitle.text = mainTitle;

        subTitle = [object objectForKey:@"Type"];
        cell.detail.text = subTitle;

         // Implement this if you want to Show image
         cell.showImage.image = [UIImage imageNamed:@"AppIcon.png"];

         PFFile *imageFile = [object objectForKey:@"imageFile"];

         if (imageFile) {
         cell.showImage.file = imageFile;
         [cell.showImage loadInBackground];
         }
    }


    #pragma mark - UITableViewDelegate

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {



        [tableView deselectRowAtIndexPath:indexPath animated:YES];

        [searchedBar resignFirstResponder];

        if ([self.objects count] == indexPath.row) {
            [self loadNextPage];
        } else {
            PFObject *photo = [self.objects objectAtIndex:indexPath.row];
            NSLog(@"%@", photo);

            // Do something you want after selected the cell
        }
    }



    #pragma mark - UIScrollViewDelegate


    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
        [self.searchedBar resignFirstResponder];
    }

    - (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
        [self.searchedBar resignFirstResponder];
        [self queryForTable];
        [self loadObjects];

    }







    @end
1

There are 1 answers

8
Mahmoud Adam On BEST ANSWER

Your code does not point to an existing job object

PFObject *jobs = [PFObject objectWithClassName:@"Jobs"];

To add an object to a relation it should be an existing object on its table (if new object, you need to save it first)

UPDATE:

your code should use the property job in the view controller and add it to the current user relation as the following

- (IBAction)favoriteButtonAction:(id)sender {
    PFUser *user = [PFUser currentUser];
    PFRelation *relation = [user relationForKey:@"Favorites"];
    [relation addObject:job];
    [user saveInBackground];
}

It would be better if you used Parse subclasses for Job object

#import <Parse/Parse.h>
#import <Parse/PFObject+Subclass.h>

@interface Job : PFObject<PFSubclassing>
@property (nonatomic, strong) NSString *rotation;
@property (nonatomic, strong) NSString *location;
@property (nonatomic, strong) NSString *type;
..
.
.
.
@end


@implementation Tracker

@dynamic rotation, location, type,....;
+ (void)load {
    [self registerSubclass];
}

+ (NSString *)parseClassName {
    return @"Job";
}
@end

then modify your code as the following

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"showJobDetail"]) {


        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        Job *job = [self.objects objectAtIndex:indexPath.row];

        JobDetailViewController *destViewController = segue.destinationViewController;
        destViewController.job = job;

    }

}