Rob Rob - 6 months ago 24
iOS Question

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.

Answer

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;