detailVewController setEditingPossesion pg180


#1

Hi,
Great book and I am learning the right way to code for the iPhone. I made what I thought would be a slight modification to the Possession class and renamed it Modules. I planned to implement a run time unit test case where the Module class keeps track of the modules and functions I am testing along with a pass/fail count. This is similar to the Xcode iPhone SimpleDrillDown example, but instead of Play, it is a Module class. As you state I appear to be struggling with how data is passed and rather than the brute force method in SimpleDrillDown, I wanted a NSMutableArray that I would create with strings passed to a addModule method, much like the random Possession class method.

@interface Module : NSObject {
NSString *moduleName;
int functionCnt;
int passCnt;
int failCnt;
int notImpCnt;
NSDate *dateTested;
}

@property (nonatomic, retain) NSString *moduleName;
@property (nonatomic) int functionCnt;
@property (nonatomic) int passCnt;
@property (nonatomic) int failCnt;
@property (nonatomic) int notImpCnt;
@property (nonatomic, retain, readonly) NSDate *dateTested;

modules_list = [NSArray arrayWithObjects:@“Applications”, @“Battery”, @“Device”, @“File”, @“HTTP”, @“Log”, @“Memory”, @“Radio”, @“SIM”, @“SMS”,@“String”, @“Thread”, @“Time”, nil];
modules = [[NSMutableArray alloc] init];
for (NSString *mod_name in modules_list) {
[modules addObject:[Module addModule]];
}

  • (id)addModule:(NSString *)name
    {
    int fcnt = 1;
    int pass = 0;
    int fail = 0;
    int notimp = 0;

    Module *newModule = [[self alloc] initWithModuleName:name
    fnCnt:fcnt
    mPass:pass
    mFail:fail
    mImp:notimp];

    return newModule;
    }

This all work fine, I can see the modules array in the debugger and it returns the proper count:

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return [modules count];
    }

However, when I try and access the class to name the table cell, the compiler complains about member struct and I can’t seem to access the module name in my class

// Can’t get here, compiler complains: not a struct or union
//cell.textLabel.text = [modules.moduleName objectAtIndex:indexPath.row];
//cell.textLabel.text = [modules_list objectAtIndex:indexPath.row];

The same class code structure/path appears to work in the SimpleDrillDown method. What am I missing? I am hoping I just don’t grok the proper syntax to get at my moduleName?

Thanks in advance.

Ned


#2

Hi there,

i think “modules.moduleName” won’t work, because you try to access a property on an array.
You first need to get the object from the array, then specify which property you want to access. Like this:

[modules objectAtIndex:indexPath.row].moduleName

of course, if there were no properties, you could not use the dot notation. Then you would need to use it like this:

[modules objectAtIndex:[indexPath row] moduleName];

//cell.textLabel.text = [modules_list objectAtIndex:indexPath.row];
should work, as the array contains only strings.

Cheers,
Richard


#3

Thanks for your response, but that does not fix the compiler error, nor does changes to the property. Fat figured the code somewhere, I will figure it out.

// Works for NSArray
cell.textLabel.text = [modules_list objectAtIndex:indexPath.row];

// Fail for NSMutableArray with a class object
//cell.textLabel.text = [modules objectAtIndex:[indexPath row] moduleName];
//cell.textLabel.text = [modules objectAtIndex:indexPath.row].moduleName;

Ned


#4

First, I’d stay away from the dot notation. From your code snippet, dot notation is confusing you. I know you may be thinking, “Well, I should learn it now because I’ll have to use it later.” Except that’s not true, I never use it, and I write Objective-C for 8+ hours a day. We were soft on dot notation in the book. I somewhat regret that. What I really wanted to write was, “Dot notation is stupid. It serves no purpose. It confuses beginners in more ways than I can count. And I can count to 100.”

Anyhow, in your one line of code:
//cell.textLabel.text = [modules objectAtIndex:[indexPath row] moduleName];

There are (supposed to be) 5 different messages being sent. By separating each message into its own line, you will see the error:

NSInteger row = [indexPath row];
Module *indexedModule = [modules objectAtIndex:row];
NSString *moduleName = [indexedModule moduleName];

UILabel *textLabel = [cell textLabel];
[textLabel setText:moduleName];

That is what you are trying to do - and this code will work. Now let’s put it back together in steps:
Step 1: No more row variable

Module *indexedModule = [modules objectAtIndex:[indexPath row]];
NSString *moduleName = [indexedModule moduleName];

UILabel *textLabel = [cell textLabel];
[textLabel setText:moduleName];

Step 2: No more indexedModule variable

NSString *moduleName = [[modules objectAtIndex:[indexPath row]] moduleName];

UILabel *textLabel = [cell textLabel];
[textLabel setText:moduleName];

Step 3: No more moduleName variable

UILabel *textLabel = [cell textLabel];
[textLabel setText:[[modules objectAtIndex:[indexPath row]] moduleName]];

Step 4: No more textLabel variable

[[cell textLabel] setText:[[modules objectAtIndex:[indexPath row]] moduleName]];

Hope that helps.


#5

All,

Besides the syntax issues, the root cause of the compiler error was due to fat fingers and not adding init routine to ItemDetailController.m, which is kinda of mentioned on page 174, but not clear it is needed on page 176. Upon inspection of the download demo code I found these missing methods . It was a very useful exercise in what the real problem is when the compiler complains.

On page 176, it should be clear that these routines need to be added:

  • (id)init
    {
    return [super initWithNibName:@“ItemDetailViewController” bundle:nil];
    }

  • (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
    {
    return [self init];
    }

In the big picture, I was able to put the unit test module and funcition data in a plist, and then create the table like so.

// A dictionary with a key of module name, and an array of funcitons for that function
NSString *fnPath = [[NSBundle mainBundle] pathForResource:@“functions” ofType:@“plist”];
NSDictionary *dictFn = [[NSDictionary alloc] initWithContentsOfFile:fnPath ];
NSArray *arrayFn = [[dictFn allKeys] sortedArrayUsingSelector:@selector(compare:)]; // If you don’t do this, the array will be unsorted as compared to the dictionary

modCnt = [arrayFn count];
modules = [[NSMutableArray alloc] init];

for (i = 0; i < modCnt; i++) {
	NSString *mod_name = [arrayFn objectAtIndex:i];
	NSArray *array = [dictFn objectForKey];
	fnCnt = [array count];
	//NSLog(@"arrayFn object cnt = %d ModName = %@ ModFns = %@, FnCnt = %d", i, mod_name, array, fnCnt);
	[modules addObject:[Module addModule:mod_name fName:array fnCnt:fnCnt]];
}
  • (id)addModule:(NSString )name fName:(NSArray)fnName fnCnt:(int)fnVal
    {
    int pass = 0;
    int fail = 0;
    int notimp = 0;

    Module *newModule = [[self alloc] initWithModuleName:name
    fName:fnName
    fnCnt:fnVal
    mPass:pass
    mFail:fail
    mImp:notimp];

    return newModule;
    }

I can now create a Application Test target for the iPhone via this template and a Logic Test Target for the views. (-;

Thanks to all who responded.

Ned


#6

Hey Ned,

Glad to hear you got the application working! I’m a little surprised by your response to the advice that was given, as well as what you think the underlying problem is. While I’m not going to address the dot notation issue (although anyone prone to “fat fingers” would probably make their lives a whole lot easier by avoiding dot notation in Objective-C, regardless of experience level), I would like to comment on what you described as “missing” methods on page 176.

For anyone following along with the book, those 2 methods do not need to be added to ItemDetailViewController.m because the default behavior works perfect as explained earlier in the book (chapter 9, pg. 130). There’s no reason to create an init method that specifies the NIB to load, or to override the designated initializer of the superclass to call that init method in this situation. ItemDetailView is a subclass of UIViewController, and the init method for UIViewController calls [self initWithNibName:nil bundle:nil].

If your view controller has a different name than your NIB, then you will have to override those methods accordingly, but when this is appropriate is clearly explained, and the ItemDetailViewController example on pg. 176 is not one of them…the code is correct exactly the way the text has it written, since the ItemDetailViewController.xib was created at the same time (and with the same name) as the ItemDetailViewController implementation file.