Search code examples
iosios5core-datansmanagedobjectnsmanagedobjectcontext

Passing objects in CoreData


I am using CoreData in my application and it is a one-to-many relationship, I have two entities called folders and files. So a folder can have many files. So my first view controller shows all folders and when clicked on each folder, I show the files present in it.

Now all the folders and files present in it are created by user dynamically and are not coming from any database or from web server.

Now I have one more view controller, which I open from app delegate, and here I want to show some files.

I know that first we should pass the managedObjectContext, which will be declared in app delegate

ViewController *View = [[ViewController alloc]initWithNibName: @"ViewController" bundle:nil]
View.managedObjectContext = self.managedObjectContext;

So if we just pass managedObjectContext, can we access the file entity objects, OR we have to pass file entity object also.

Now as you know, I have two entities, called Folder and File, now to access files stored in any folder, I just do this.

NSMutableArray *filesArray = [self.folder.file mutableCopy];

But now, I want to show the files in anyView I want, which can be opened after two three navigations or it is opened directly from appDelegate.m.

To be very clear , my question is how to do this? i.e how can I get filesArray from AppDelegate.

Based on Below Answer edited. I have created a fetchResultsController this way

- (NSFetchedResultsController *)fetchedResultsController {

    if (m_fetchResultsController != nil) {
        return m_fetchResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"File" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"alarm" ascending:YES];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"alarm!= nil"]];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    aFetchedResultsController.delegate = self;
    self.fetchResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchResultsController performFetch:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    return  m_fetchResultsController;   
}

and in ViewDidLoad and I written this way

- (void)viewDidLoad
{
    [super viewDidLoad];
   NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }


     NSMutableArray *alarms = [[self.fetchResultsController fetchedObjects]mutableCopy];

    NSLog(@"alarmsCount:%d",[alarms count]);
}

Regards Ranjit.


Solution

  • If you share more details we could help you, but in the meantime...

    If the master controller shows the folder and the details controller show the files, simply inject a reference of the selected folder to the detail controller.

    For example, create a property in the detail controller like this (it is needed to be synthesized)

    @property (nonatomic,strong) Folders* currentFolder;
    

    When you create a detail controller, do the following

    DetailController* detCtr = // alloc-init here
    detCtr.currentFolder = // the folder you want to pass in
    

    Some Notes

    Use Camel Case notation. Use NSFetchedResultsController for lazy loading data in association with tables, etc. Rename entity Files and Folders in their singular form.

    Edit

    Ok, so if you want to access your app delegate from everywhere within the app, you can just call

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [appDelegate someMethod];
    

    Here, you can retrieve the folder you are interesting in with a public property declared in .h and synthesized in .m.

    Now, this in not a so good approach to follow. AppDelegate class would remain as is.

    Another, more elegant, approach is to create a singleton called SharedManager (or something else) that store the currentFolder that need to visualize.

    Shared Manager will look like

    //.h
    @property (nonatomic,strong) Folder* currentFolder;
    
    + (SharedManager*)sharedInstance;
    
    
    //.m
    @synthesize currentFolder;
    
    + (SharedManager*)sharedInstance
    {
        static dispatch_once_t once;
        static id sharedInstance;
        dispatch_once(&once, ^{
            sharedInstance = [[SharedManager alloc] init];
        });
        return sharedInstance;
    }
    

    That, after importing it, it will be used to set a folder as the current one

    [[SharedManager sharedInstance] setCurrentFolder:theFolderYouWantToShare];
    

    or to retrieve the current folder

    Folder* theSharedFolder = [[SharedManager sharedInstance] currentFolder];
    

    P.S. Do not abuse on Singletons. The singleton pattern I use requires iOS 4 and greater.

    Edit 2

    It was my fault. I did not understand the question.

    If you need to retrieve all the files, you could just create a NSFetchRequest like the following:

    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"File" inManagedObjectContext:context]; 
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    
    NSError *error = nil; 
    NSArray *results = [context executeFetchRequest:request error:&error];
    

    where results will contain all the files in your store indipendently from any folder. You can use this array to display files in your table.

    If you have a lot of files, I really suggest to take a look at NSFetchedResultsController class. It allows lazy loading together with UITableView.

    You can find a good tutorial here: How To Use NSFetchedResultsController

    Edit 3

    How to go about reminders?

    You should provide to your request (the one you use with NSFetchedResultsController) a predicate like the following

    [request setPredicate:[NSPredicate predicateWithFormat:@"reminder != nil"]];
    

    In this manner you retrieve the files where reminder is NOT nil. reminder is the property you are using in the model.

    P.S. Check the code because I've written without Xcode support.