No visible @interface for 'BNRItem' declares the selector


#1

So I am getting a seemingly unconquerable error- one that has kept me agitated for the past couple hours. I feel as though I have copied code straight out of the book, and am now wondering if there is perhaps something different in the syntax of the newest SDK vs. iOS5 that is causing this error. Basically I get an error for each of the following two lines of code from DetailViewController.m:

[item setImageKey:key];

[[BNRImageStore sharedStore] setImage:image
forKey:[item imageKey]];

The following are the errors respectively:

[color=#FF0000]No visible interface for ‘BNRItem’ declares the selector ‘setImageKey:’[/color]

and

[color=#FF0000]No visible interface for ‘BNRItem’ declares the selector ‘ImageKey:’[/color]

I have attached my code, any help would be greatly appreciated! I am stumped! I think it has something to do with the fact that the DetailViewController is not recognizing the BNRItem instance “item” but I’m not sure why as I have declared the property for imageKey and synthesized it.

Note: I have stopped on p.253 as this is where the error occurred. I strongly believe that my code is correct up until this point, as I did not receive errors until those two lines.

BNRItem.h

[code]//
// BNRItem.h
// RandomPosessions
//
// Created by Blake Jennings on 6/18/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface BNRItem : NSObject
//{
// NSString *itemName;
// NSString *serialNumber;
// int valueInDollars;
// NSDate *dateCreated;
//
// BNRItem *containedItem;
// __weak BNRItem *container; //Parent-child relationship… __weak used to avoid retain cycle
// //Retain cycles are rare, and there is a tool called “Leaks” to help us find them.
//}

  • (id)initWithItemName:(NSString *)name
    valueInDollars:(int)value
    serialNumber:(NSString *)sNumber;

  • (id)initWithItemName:(NSString *)name
    serialNumber:(NSString *)sNumber;

  • (id)randomItem;

/*

  • (void)setContainedItem:(BNRItem *)i;

  • (BNRItem *)containedItem;

  • (void)setContainer:(BNRItem *)i;

  • (BNRItem *)container;

  • (void)setItemName:(NSString *)str;

  • (NSString *)itemName;

  • (void)setSerialNumber:(NSString *)str;

  • (NSString *)serialNumber;

  • (void)setValueInDollars:(int)i;

  • (int)valueInDollars;

  • (NSDate *)dateCreated;

*/

@property (nonatomic, strong) BNRItem *containedItem;
@property (nonatomic, weak) BNRItem *container;

@property (nonatomic, copy) NSString *itemName;
@property (nonatomic, copy) NSString *serialNumber;
@property (nonatomic) int valueInDollars;
@property (nonatomic, readonly, strong) NSDate *dateCreated;

@property (nonatomic, copy) NSString *imageKey;

@end
[/code]

BNRItem.m

[code]//
// BNRItem.m
// RandomPosessions
//
// Created by Blake Jennings on 6/18/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import “BNRItem.h”

@implementation BNRItem
@synthesize itemName;
@synthesize containedItem, container, serialNumber, valueInDollars, dateCreated;
@synthesize imageKey;

  • (id)randomItem
    {
    // Create an array of three adjectives
    NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@“Fluffy”,@“Rusty,”,@“Shiny”, nil];

    // Create an array of three nouns
    NSArray *randomNounList = [NSArray arrayWithObjects:@“Bear”,@“Spork”,@“Mac”, nil];

    // Get the index of a random adjective/noun from the lists
    // Note: the % operator, called the modulo operater, gives you the remainder
    // So adjectiveIndex is a random number from 0 to 2 inclusive.

    NSInteger adjectiveIndex = rand() % [randomAdjectiveList count];
    NSInteger nounIndex = rand() % [randomNounList count];

    // Note that NSInteger is not an object, but a type definition for “unsigned long”

    NSString *randomName = [NSString stringWithFormat:@"%@ %@", [randomAdjectiveList objectAtIndex:adjectiveIndex], [randomNounList objectAtIndex:nounIndex]];

    int randomValue = rand() % 100;

    NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c", ‘0’ + rand() %10, ‘A’ + rand() % 26, ‘0’ + rand() % 10, ‘A’ + rand() %26, ‘0’ + rand() % 10];

    BNRItem *newItem = [[self alloc] initWithItemName:randomName valueInDollars:randomValue serialNumber:randomSerialNumber];
    return newItem;
    }

  • (id)init
    {
    return [self initWithItemName:@“Item” valueInDollars:0 serialNumber:@""];
    }

  • (id)initWithItemName:(NSString *)name valueInDollars:(int)value serialNumber:(NSString *)sNumber
    {
    //Call the superclass’s designated initializer
    self = [super init];

    // Did the super cass’s designated initializer succeed?
    if (self){

    //Give the instance variables initial values
    [self setItemName:name];
    [self setSerialNumber:sNumber];
    [self setValueInDollars:value];
    dateCreated = [[NSDate alloc]init];
    }

    // Return the address of the newly initialized object
    return self;
    }

-(id)initWithItemName:(NSString *)name serialNumber:(NSString *)sNumber
{
//Call the superclass’s designated initializer
self = [super init];

if (self){
    
    [self setItemName:name];
    [self setSerialNumber:sNumber];
}
return self; 

}

/*

  • (void)setItemName:(NSString *)str
    {
    itemName = str;
    }

  • (NSString *)itemName
    {
    return itemName;
    }

  • (void)setSerialNumber:(NSString *)str
    {
    serialNumber = str;
    }

  • (NSString *)serialNumber
    {
    return serialNumber;
    }

  • (void)setValueInDollars:(int)i
    {
    valueInDollars = i;
    }

  • (int)valueInDollars
    {
    return valueInDollars;
    }

  • (NSDate *)dateCreated
    {
    return dateCreated;
    }
    */

  • (NSString *) description
    {
    NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@ (%@): Worth $%d, recorded on %@", itemName, serialNumber, valueInDollars, dateCreated];
    return descriptionString;
    }

  • (void)setContainedItem:(BNRItem *)i
    {
    containedItem = i;

    //When given an item to contain, the contained item will be given a pointer to its container
    [i setContainer:self];
    }
    /*

  • (BNRItem *)containedItem
    {
    return containedItem;
    }

  • (void)setContainer:(BNRItem *)i
    {
    container = i;
    }

  • (BNRItem *)container
    {
    return container;
    }

*/

@end
[/code]

BNRItemStore.h

[code]//
// BNRItemStore.h
// Homepwner
//
// Created by Blake Jennings on 7/26/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import <Foundation/Foundation.h>

@class BNRItem;

@interface BNRItemStore : NSObject
{
NSMutableArray *allItems;
}
// Notice that this is a class method and prefixed with a + instead of a -

  • (BNRItemStore *)sharedStore;
  • (NSArray *)allItems;

  • (BNRItem *)createItem;

  • (void)removeItem:(BNRItem *)p;

  • (void)moveItemAtIndex:(int)from
    toIndex:(int)to;

@end
[/code]

BNRItemStore.m

[code]//
// BNRItemStore.m
// Homepwner
//
// Created by Blake Jennings on 7/26/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import “BNRItemStore.h”
#import “BNRItem.h”

@implementation BNRItemStore

  • (id)init
    {
    self = [super init];
    if (self) {
    allItems = [[NSMutableArray alloc] init];
    }
    return self;
    }
  • (BNRItemStore *)sharedStore
    {
    static BNRItemStore *sharedStore = nil;
    if (!sharedStore)
    sharedStore = [[super allocWithZone:nil] init];

    return sharedStore;
    }

  • (id)allocWithZone:(NSZone *)zone
    {
    return [self sharedStore];

}

  • (NSArray *)allItems
    {
    return allItems;
    }

-(BNRItem *)createItem
{
BNRItem *p = [BNRItem randomItem];
[allItems addObject:p];
return p;
}

  • (void)removeItem:(BNRItem *)p
    {
    [allItems removeObjectIdenticalTo:p];
    }

  • (void)moveItemAtIndex:(int)from toIndex:(int)to
    {
    if (from == to){
    return;
    }
    // Get pointer to object being moved so we can re-insert it
    BNRItem *p = [allItems objectAtIndex:from];

    // Remove p from array
    [allItems removeObjectAtIndex:from];

    // Insert p at new location
    [allItems insertObject:p atIndex:to];
    }

@end
[/code]

ItemsViewController.h

[code]//
// ItemsViewController.h
// Homepwner
//
// Created by Blake Jennings on 7/26/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface ItemsViewController : UITableViewController

{

}

  • (IBAction)addNewItem:(id)sender;

@end
[/code]

ItemsViewController.m

[code]//
// ItemsViewController.m
// Homepwner
//
// Created by Blake Jennings on 7/26/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import “ItemsViewController.h”
#import “BNRItemStore.h”
#import “BNRItem.h”
#import “DetailViewController.h”

@implementation ItemsViewController

  • (void)tableView:(UITableView *)aTableView
    didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    DetailViewController *detailViewController = [[DetailViewController alloc] init];

    NSArray *items = [[BNRItemStore sharedStore] allItems];
    BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];

    // Give detail view controller a pointer to the item object in row
    [detailViewController setItem:selectedItem];

    // Push it onto the top of the navigation controller’s stack
    [[self navigationController] pushViewController:detailViewController animated:YES];
    }

  • (id) init
    {
    // Call the superclass’s designated initialzer
    self =[super initWithStyle:UITableViewStyleGrouped];
    if (self){
    UINavigationItem *n = [self navigationItem];
    [n setTitle:@“Homepwner”];

      // Create a new bar button item that will send addNewItem: to ItemsView Controller
      UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewItem:)];
      // Set this bar button item as the right item in the navigationItem
      [[self navigationItem] setRightBarButtonItem:bbi];
      [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]]; 
    

    }
    return self;
    }

  • (id) initWithStyle:(UITableViewStyle)style
    {
    return [self init];
    }

// This will ensure that all instances of ItemsViewContrller use the UITableViewStyelGrouped sytle, no matter what initialization method is sent to it. Nice!

  • (NSInteger)tableView: (UITableView *)tableView
    numberOfRowsInSection:(NSInteger)section
    {
    return [[[BNRItemStore sharedStore] allItems] count];
    }

  • (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    // Create an instance of UITableViewCell, with default appearance
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@“UITableViewCell”];

    // If there is no reusable cell of this type, create a new one
    if (!cell) {
    cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@“UITableViewCell”];
    }

    // Set the text on tehe cell with the description of the item that is at the nth index of items, where n = row this cell will appear in on the tableview
    BNRItem *p = [[[BNRItemStore sharedStore] allItems] objectAtIndex:[indexPath row]];
    [[cell textLabel] setText:[p description]];

    return cell;

}

  • (IBAction)addNewItem:(id)sender
    {
    // Make a new index path for the 0th section, last row

    // Create a new BNRItem and add it to the store
    BNRItem *newItem = [[BNRItemStore sharedStore] createItem];

    // Figure out where that item is in the array
    int lastRow = [[[BNRItemStore sharedStore] allItems] indexOfObject:newItem];

    NSIndexPath *ip = [NSIndexPath indexPathForRow:lastRow inSection:0];

    // Insert this row into the table
    [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:ip] withRowAnimation:UITableViewRowAnimationTop];

}

  • (void)tableView:(UITableView *)tableView
    commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
    // If the table view is asking to commit a delete command…
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
    BNRItemStore *ps = [BNRItemStore sharedStore];
    NSArray *items = [ps allItems];
    BNRItem *p = [items objectAtIndex:[indexPath row]];
    [ps removeItem:p];

      // We also remove that row from the table view with an animation
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
    

    }
    }

  • (void)tableView:(UITableView *)tableView
    moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
    {
    [[BNRItemStore sharedStore] moveItemAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]];
    }

-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear];
[[self tableView] reloadData];
}

@end
[/code]

DetailViewController.h

[code]//
// DetailViewController.h
// Homepwner
//
// Created by Blake Jennings on 8/1/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import <UIKit/UIKit.h>
#import “BNRItem.h”

@interface DetailViewController : UIViewController
<UINavigationControllerDelegate, UIImagePickerControllerDelegate>

{

__weak IBOutlet UITextField *nameField;

__weak IBOutlet UITextField *serialNumberField;

__weak IBOutlet UITextField *valueField;

__weak IBOutlet UILabel *dateLabel;

__weak IBOutlet UIImageView *imageView;

}

@property (nonatomic, strong) BNRItem *item;

  • (IBAction)takePicture:(id)sender;

@end
[/code]

DetailViewController.m

[code]//
// DetailViewController.m
// Homepwner
//
// Created by Blake Jennings on 8/1/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import “DetailViewController.h”
#import “BNRItem.h”
#import “BNRImageStore.h”

@implementation DetailViewController

@synthesize item;

  • (void)setItem:(BNRItem *)i
    {
    item = i;
    [[self navigationItem] setTitle:[item itemName]];
    }

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    [[self view] setBackgroundColor: [UIColor groupTableViewBackgroundColor]];
    }

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

    [nameField setText:[item itemName]];
    [serialNumberField setText:[item serialNumber]];
    [valueField setText:[NSString stringWithFormat:@"%d", [item valueInDollars]]];

    // Create a NSDateFormatter that will turn a date into a simple date string
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

    // Use filtered NSDate object to set dateLabel contents
    [dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
    }

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

    // Clear first responder
    [[self view] endEditing:YES];

    // “SAVE” CHANGES TO ITEM
    [item setItemName:[nameField text]];
    [item setSerialNumber:[serialNumberField text]];
    [item setValueInDollars:[[valueField text] intValue]];

}

  • (IBAction)takePicture:(id)sender
    {
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

    // If our device has a camera we want to take a picture, otherwise, we just pick from photo library
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
    [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
    } else {
    [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
    }
    // This line of code will generate a warning right now, ignore it.
    [imagePicker setDelegate:self];

    // Place image picker on the screen
    [self presentViewController:imagePicker animated:YES completion:nil];
    }

  • (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
    // Get picked image from info dictionary
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

    // Create a CFUUID obeject - it knows how to create unique identfier strings
    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);

    // Create a string from the unique identifier
    CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID);

    // Use that uniuqe ID to set our item’s imageKEy
    NSString *key = (__bridge NSString *)newUniqueIDString;
    [item setImageKey:key];

    [[BNRImageStore sharedStore] setImage:image
    forKey:[item imageKey]];

    // Put that image onto the screen in our image view
    [imageView setImage:image];

    // Take image picker off the screen - you must call this dismiss method
    [self dismissViewControllerAnimated:YES completion:nil];
    }

@end
[/code]

BNRImageStore.h

[code]//
// BNRImageStore.h
// Homepwner
//
// Created by Blake Jennings on 8/1/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import <Foundation/Foundation.h>
#import “BNRItem.h”

@interface BNRImageStore : NSObject
{
NSMutableDictionary *dictionary;
}

  • (BNRImageStore *)sharedStore;
  • (void)setImage:(UIImage *)i forKey:(NSString *)s;
  • (UIImage *)imageForKey:(NSString *)s;
  • (void)deleteImageForKey:(NSString *)s;

@end
[/code]

BNRImageStore.m

[code]//
// BNRImageStore.m
// Homepwner
//
// Created by Blake Jennings on 8/1/12.
// Copyright © 2012 Blake Jennings. All rights reserved.
//

#import “BNRImageStore.h”
#import “BNRItem.h”

@implementation BNRImageStore

  • (id)allocWithZone:(NSZone *)zone
    {
    return [self sharedStore];
    }

  • (BNRImageStore *)sharedStore
    {
    static BNRImageStore *sharedStore = nil;
    if (!sharedStore) {
    // Create the singleton
    sharedStore = [[super allocWithZone:NULL] init];
    }
    return sharedStore;
    }

  • (id)init
    {
    self = [super init];
    if (self){
    dictionary = [[NSMutableDictionary alloc] init];
    }

    return self;
    }

  • (void)setImage:(UIImage *)i forKey:(NSString *)s
    {
    [dictionary setObject:i forKey:s];
    }

  • (UIImage *)imageForKey:(NSString *)s
    {
    return [dictionary objectForKey:s];
    }

  • (void)deleteImageForKey:(NSString *)s
    {
    if (!s)
    return;
    [dictionary removeObjectForKey:s];
    }

@end
[/code]


#2

Yes! :smiley:

I had duplicate files of BNRItem.h and BNRItem.m inside the ‘Homepwner’ folder. Compiler was looking at older versions.

All fixed. Thanks anyway!


#3

Same problem. How frustrating. Thanks for the answer. Also, do you remember how you got found out what you were doing wrong(having dup files)?