Search code examples
ioscore-datansarrayuipickerview

IOS: fetch array from core data for uipicker


I am trying to have a UIPickerController display some users. It works easily enough if I hard code some sample users as follows:

  //in viewDidLoad
       _users = @[@"Ronald", @"David",
                          @"Sharon", @"Amir", @"Atul"]; //etc.

However, I want to draw these users from core data. So far I have tried a simple fetch and also nsfetchedresults controller and nothing seems to work. What is the preferred way of getting an array from core data.

//fetchrequest

  - (id) getUsers{
    NSFetchRequest *fetchRequest = [NSFetchRequest 

fetchRequestWithEntityName:@"User"];
    fetchRequest.resultType = NSDictionaryResultType;


    NSError *error      = nil;
    self.managedObjectContext = [chModel sharedInstance].managedObjectContext;

    NSArray *results    = [self.managedObjectContext executeFetchRequest:fetchRequest
                                                                   error:&error];

    return results;
}

//this throws an error at

    - (NSString *)pickerView:(UIPickerView *)pickerView
                 titleForRow:(NSInteger)row
                forComponent:(NSInteger)component
    {
        NSLog(@"in titleforrow");
        return _users[row];//error here.  also error if I substitute [0]

    }

//when I try


_users = [self.fetchedResultsController fetchedObjects];
}

frc is never called


Solution

  • Two suggestions buddy :)

    1. If you have declared a property named doers (which you should have assuming from your getter declaration :) ) declare it to be of type NSArray rather than id :)

    2.Change your getter for doers to return an array rather than id :). Does not make any sense to return an Array and declare return type as id and then final re-casting it to array to access its entities using subscript syntax :)

    Issue in your code

    _doers is a instance variable :) self.doers is a property :)

    When you synchronize your property using @synchronize (now you don't write it anymore as xcode writes one for you by default ) instance variables which have same name as property but an underscore prefixed to it are used to provide the default setter and getter to your property :)

    When you call _doers you are accessing the instance variable and not property, so your getter will never be called :)

    How to solve it ??

    instead of calling return _doers[row]; call self.doers[row]

    Will it solve the issue??

    Nope :). It returns an array of user entity objects :) not the array of string :) So if you simply say self.doers[row] you will return an object of User (Dictionary in your case ) to a method which accepts a string :)

    What is the correct way then ??

    - (NSString *)pickerView:(UIPickerView *)pickerView
                     titleForRow:(NSInteger)row
                    forComponent:(NSInteger)component
        {
            User *userInstance =  (User *)self.doers[row];
            return userInstance.name; //user entiy attribute which holds name or something like that 
        }
    

    OR

    - (NSString *)pickerView:(UIPickerView *)pickerView
                     titleForRow:(NSInteger)row
                    forComponent:(NSInteger)component
        {
            return [self.doers[row] valueForKey:@"name"] //user entiy attribute which holds name or something like that 
        }
    

    it will work :) though very poor model of using getter for a property :)

    Is this approach correct ??

    Will it work ?? Of course! ... is this correct way to do it ?? No way! :)

    For every row in your picker this method gets called because you are accessing property self.doers its getter called for each cell :) which means you will query so many times your core data :)

    I think its un-necessary :) What you can do rather is to user NSFetchedResultsController a singletone variable perform fetch once in viewdidload or viewwillappear as per your requirement and use its feched objects array for your picker :)

    else

    move the code in getter of doers to a method call it once and populate your property once and once filled use the property to fill the picker :)

    Coredata operations are costly :)

    Anyway happy coding, all the best :)

    Declaimer: Code provided above is only for explaining concepts and not for the purpose of copy paste :) check syntax before using it blindly :)