I am working on my first app and am in need of some assistance. I've read through tons of similar questions on SO but just not getting anywhere.
I have a simple table view controller which has a plus button; when pressed, that leads to a modal view controller asking the user to insert information into 4 separate fields. When the user clicks save, the modal view dismisses and the information is displayed in the table view because the save button calls the NSManagedObject subclasses and through Core Data, it saves it.
I'm trying to have it so that when a user types into the first field (name), if they have already typed that name before (if they added it to Core Data with the save method), it auto-populates and shows a hidden table view with entries matching that name. I first started working with a NSMutableArray but thanks to Jeff's comments, that would not persistently keep the data, so because I already have the Core Data functionality, it makes more sense to use that. I am editing this post to include how my Core Data is currently set up.
I basically want to achieve this but with Core Data (http://www.dalmob.org/2011/03/01/alternative-autocomplete-uitextfield/)
There is a Information Entity with a relationship to the People Entity.
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
Information *information = [NSEntityDescription insertNewObjectForEntityForName:@"Information" inManagedObjectContext:context];
People *enteredPerson = (People *)[People personWithName:self.nameTextField.text inManagedObjectContext:context];
information.whichPerson = enteredPerson;
NSError *error = nil;
if (![context save:&error])
{
NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
The enteredPerson calls the personWithName method in the People NSManagedObjectSubclass:
+ (People *)personWithName:(NSString *)name inManagedObjectContext:(NSManagedObjectContext *)context
{
People *people = nil;
// Creating a fetch request to check whether the name of the person already exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"People"];
request.predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *fetchedPeople = [context executeFetchRequest:request error:&error];
if (!fetchedPeople)
{
// Handle Error
}
else if (![fetchedPeople count])
{
// If the person count is 0 then let's create it
people = [NSEntityDescription insertNewObjectForEntityForName:@"People" inManagedObjectContext:context];
people.name = name;
}
else
{
// If the object exists, just return the last object .
people = [fetchedPeople lastObject];
}
return people;
}
Based on the suggestion to create the NSFetchRequest, I am wondering the best technique to do this.
Do I do this in the Save method of the Add Entry at the end to something like this:
// NSFetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Specifiy a predicate here if there are certain conditions your fetch must adhere to
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY name CONTAINS[c] %@", self.nameTextField.text];
[fetchRequest setPredicate:predicate];
//NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle error
}
if ([fetchedObjects count] == 0)
{
// Add entry to results
}
What I want to achieve is, from Core Data, when the user types in the name, reference core data (with a fetch request) and if that name exists, as the user starts typing, populate the Table view that sits below the Text field.
Any guidance would be appreciated.
EDIT: I have updated an answer with some further code to almost get this working.
EDIT: More Code:
Property Declarations in .h
@property (retain, nonatomic) IBOutlet UITextField *nameTextField;
@property (nonatomic, retain) NSString *substring;
@property (weak, nonatomic) IBOutlet UITableView *testTableView;
@property (nonatomic, retain) NSFetchedResultsController* autocompleteFetchedResultsController;
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring;
ViewDidLoad
- (void)viewDidLoad
{
NSError *error;
if (![[self autocompleteFetchedResultsController] performFetch:&error])
{
NSLog(@"Unresolved error %@ %@", error, [error userInfo]);
exit(-1);
}
self.testTableView.delegate = self;
self.testTableView.dataSource = self;
self.testTableView.hidden = YES;
self.testTableView.scrollEnabled = YES;
self.nameTextField.delegate = self;
[super viewDidLoad];
}
Save Method
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
People *enteredPerson = (People *)[People personWithName:self.nameTextField.text inManagedObjectContext:context];
transaction.whoFrom = enteredPerson;
NSError *error = nil;
if (![context save:&error])
{
NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Thanks,
I guess self.autocompleteUrls is the NSMutableArray u had previously... Ok, U have come a long way, now see the autocompleteFetchedResultsController -> that is what fetches, and the condition if (_autocompleteFetchedResultsController != nil) protects property method from being called every time U reference autocompleteFetchedResultsController. So U should do something like this:
- (void)searchAutocompleteEntriesWithSubstring:(NSString *)substring {
_autocompleteFetchedResultsController = nil;
[self autocompleteFetchedResultsController];
[self.testTableView reloadData];
}
and If U done everything else correctly that should be it...
Your cellFoRowAtIndexPath should look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"autocomplete cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewStylePlain reuseIdentifier:CellIdentifier];
}
People *people = [self.autocompleteFetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = people.name;
return cell;
}