Search code examples
iosobjective-cnsmanagedobjectcontextnsfetchrequest

How to make a conditional request with NSManagedObjectContext?


I'm new to NSManagedObjectContext. I have created an entity Link in my app, which contains a NSString *url.

At some point of my app, I need to insert a new Link in my base, so I simply do this :

Link *link = [NSEntityDescription 
                         insertNewObjectForEntityForName:@"Link" 
                         inManagedObjectContext:self.managedObjectContext];
link.url = myUrl;

But before doing this, I want to check if there is not already a Link in my base with the same url. And I have no idea of how to do so... what should I do?

EDIT : to retrieve data from the base I'm using this method from a tool I found on the web:

// Fetch objects with a predicate
+(NSMutableArray *)searchObjectsForEntity:(NSString*)entityName withPredicate:(NSPredicate *)predicate andSortKey:(NSString*)sortKey andSortAscending:(BOOL)sortAscending andContext:(NSManagedObjectContext *)managedObjectContext
{
    // Create fetch request
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];

    // If a predicate was specified then use it in the request
    if (predicate != nil)
        [request setPredicate:predicate];

    // If a sort key was passed then use it in the request
    if (sortKey != nil) {
        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
        [request setSortDescriptors:sortDescriptors];
    }

    // Execute the fetch request
    NSError *error = nil;
    NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];

    // If the returned array was nil then there was an error
    if (mutableFetchResults == nil)
        NSLog(@"Couldn't get objects for entity %@", entityName);

    // Return the results
    return mutableFetchResults;
}

I would like to know how to use it.

Thanks for your help.


Solution

  • The method you provided just searches for a NSManagedObject that matches the attributes in the NSManagedObjectContext and if one exists, it returns it.

    But, what you need to implement is called the Find-or-Create pattern, which is discussed in the Core Data programming guide. Basically, you search for an object matching specific criteria and if it exists that object is returned. If that object does not exist you create it.

    Core Data Programming Guide

    E.g.

    + (NSString *)entityName
    {
        return NSStringFromClass([Link class]);
    }
    
    + (instancetype)findUsingPredicate:(NSPredicate *)predicate withContext:(NSManagedObjectContext *)context
    {
        Link * entity;
    
        // Setup the fetchRequest
        NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[[self class] entityName]];
        fetchRequest.predicate = predicate;
    
        // Credit: @Martin R
        [fetchRequest setFetchLimit:1];
    
        // Execute the fetchRequest
        NSError *error = nil;
        NSArray * matchingLinks = [context executeFetchRequest:fetchRequest error:&error];
    
        // MatchingLinks will only return nil if an error has occurred otherwise it will return 0
        if (!matchingLinks)
        {
            // handle error
            // Core data returns nil if an error occured
            NSLog(@"%s %@", __PRETTY_FUNCTION__, [error description]);
        }
        else if ([matchingLinks count] <= 0)
        {
            // if the count <= 0, there were no matches
    
            NSLog(@"%s Not found", __PRETTY_FUNCTION__);
    
        } else {
    
            // A link with a url that matches the url in the dictionary was found.
            NSLog(@"%s Found", __PRETTY_FUNCTION__);
    
            entity = [matchingLinks lastObject];
        }
    
        return entity;
    }
    
    + (instancetype)findUsingPredicate:(NSPredicate *)predicate orCreateWithContext:(NSManagedObjectContext *)context
    {
        Link * entity = [[self class] findUsingPredicate:predicate withContext:context];
    
        if (!entity) {
            entity = [[self class] createWithContext:context];
        }
    
        return entity;
    }
    
    + (isntancetype)createWithContext:(NSManagedObjectContext *)context
    {
        return [[self class] alloc] initWithContext:context];
    }
    
    - (instancetype)initWithContext:(NSManagedObjectContext *)context
    {
        Link * entity = [NSEntityDescription insertNewObjectForEntityForName:[[self class] entityName] inManagedObjectContext:context];
    
        return entity;
    }
    

    USE CASE:

    NSString * url = @"http://www.mylink.com";
    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"url = %@", url];
    Link * link = [Link findUsingPredicate:predicate orCreateWithContext:self.managedObjectContext];
    link.url = url;