Challenge: New Asset Types Solution


#1

Since there is no solution as of yet i will present mine

In AssetTypePicker.h added a UITextField and UIAlertViewDelegate

[code]@interface AssetTypePicker : UITableViewController {

Possession *possession;
UITextField *myTextField;

}
[/code]

In AssetTypePicker.m changed i(id)init to display a button on the navigationbar

[code]-(id)init {

self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
    UIBarButtonItem *bbi = [[UIBarButtonItem alloc] 
                            initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
                            target:self 
                            action:@selector(addNewAsset:)];
    
    // Set this bar button item as the right item in the navigationItem
    [[self navigationItem] setRightBarButtonItem:bbi];
    
    // The navigationItem retains its buttons, so bbi can be released 
    [bbi release];
}    
return self;

}
[/code]

Added the action for the new asset button and delegate for alertView when button is pressed

[code]- (IBAction)addNewAsset:(id)sender
{
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:@“New Asset” message:@“this gets covered” delegate:self cancelButtonTitle:@“Cancel” otherButtonTitles:@“OK”, nil];
myTextField = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 45.0, 260.0, 25.0)];

[myTextField setBackgroundColor:[UIColor whiteColor]];
[myAlertView addSubview:myTextField];
[myAlertView show];
[myAlertView release];

}

  • (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
    if (buttonIndex == 1) {
    [[PossessionStore defaultStore] addAssetofType:[myTextField text]];
    [[self tableView] reloadData];

    }
    [myTextField release];
    }
    [/code]

Inside PossessionStore.h added the following method

....
-(void)fetchPossesionsIfNecessary;

#pragma mark Asset types
-(NSArray*)allAssetTypes;

-(BOOL)addAssetofType:(NSString *)assetType;

@end[/code]

Added the addAssetofType function. it works but im trying to find a way to eliminate the for loop and the go tag to make it cleaner
[code]-(BOOL)addAssetofType:(NSString *)assetType
{

    // go through all assets and find the key
    BOOL go = YES;
    
    // loops through all assetTypes and see's if there is a string equal
    for (int i =0; i< [allAssetTypes count]; ++i) {
        if ([[[allAssetTypes objectAtIndex:i]valueForKey:@"label"]isEqualToString:assetType]) {
            go = NO;
        }
    }
    if (go) {
        NSManagedObject *type;
        type = [NSEntityDescription insertNewObjectForEntityForName:@"AssetType" inManagedObjectContext:context];
        [type setValue:assetType forKey:@"label"];
        [allAssetTypes addObject:type];
        return YES;
    } else {
        NSLog(@"asset Type %@ all ready exist",assetType);
        return NO;
    }
}

Babam hope it helps anyone stuck :slight_smile:


#2

Nice one - thanks for posting

You could eliminate the go tag as once you’ve found a match you don’t need to loop any further:

    // loops through all assetTypes and see's if there is a string equal
    
    for (NSManagedObject *type in allAssetTypes) {
        if ([[type valueForKey:@"label"] isEqualToString:assetType]) {
            NSLog(@"asset Type %@ all ready exist",assetType);
            return NO;
        }
    }
    
    NSManagedObject *type;
    type = [NSEntityDescription insertNewObjectForEntityForName:@"AssetType" inManagedObjectContext:context];
    [type setValue:assetType forKey:@"label"];
    [allAssetTypes addObject:type];
    return YES;

I also looked for other ways of achieving the same thing as this is a fairly standard requirement and came up with two other options (not saying these are better or cleaner - just different)

This one fires off multiple threads to search the array

    NSUInteger index = [allAssetTypes indexOfObjectWithOptions:NSEnumerationConcurrent 
                                                   passingTest:^(id obj, NSUInteger idx, BOOL *stop) 
                        {
                            NSManagedObject *type = (NSManagedObject *)obj;
                            if ([[type valueForKey:@"label"] isEqualToString:assetType]) {
                                *stop = YES;
                                return YES;
                            }
                            return NO;
                        }];
    
    if (index == NSNotFound) 
    {
        NSManagedObject *type;
        type = [NSEntityDescription insertNewObjectForEntityForName:@"AssetType" inManagedObjectContext:context];
        [type setValue:assetType forKey:@"label"];
        [allAssetTypes addObject:type];
        return YES;
    } else {
        NSLog(@"asset Type %@ all ready exist",assetType);
        return NO;
    }

and this one uses a predicate to search

    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
    [request setFetchLimit:1];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"AssetType" inManagedObjectContext:context];
    [request setEntity:entity];
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat: @"label == %@", assetType];
    [request setPredicate:predicate];
    
    NSUInteger typeCount = [context countForFetchRequest:request error:nil];
    if (typeCount == 0)
    {
        NSManagedObject *type;
        type = [NSEntityDescription insertNewObjectForEntityForName:@"AssetType" inManagedObjectContext:context];
        [type setValue:assetType forKey:@"label"];
        [allAssetTypes addObject:type];
        return YES;
    }
    else {
        NSLog(@"asset Type %@ all ready exist",assetType);
        return NO;
    }

I also tried subclassing NSManagedObject for the AssetType and using the validateForInsert method but this only seems to get triggered on the save event so wasn’t too useful for this purpose.

I’d be interested to hear if there are better ways of doing this.

Gareth


#3

Thx dude i got rid of the pesky loop!! :slight_smile:


#4

Just looking at your solution and had a suggestion. What if you used the containsObject: method of NSArray? It makes it a great deal simpler…

Just my 2 cents…


#5

Ok I found out that that didn’t work because the fact that the array doesn’t contain NSStrings, it contains NSManagedObjects…

Oh well… :wink:

One thing you could do to simplify your UIAlertView is to use the built-in text field:

[code]- (void)addNewAssetType:(id)sender
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"New Asset"
message:@"Enter a new Asset Type"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@“Ok”, nil];

[alertView setAlertViewStyle:UIAlertViewStylePlainTextInput];

[alertView show];
[alertView release];

}

  • (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
    if (buttonIndex == 1) {
    [[PossessionStore defaultStore] addNewAssetType:[[alertView textFieldAtIndex:0] text]];
    [[self tableView] reloadData];
    }
    [newAssetType release];
    }
    [/code]