+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Account''

32,682

Solution 1

- (NSManagedObjectContext *)managedObjectContext
{
    if (managedObjectContext != nil) return managedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {

        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return managedObjectContext;
}
  • You haven't provided a lazy loading implementation of persistentStoreCoordinator
  • so coordinator will always be nil
  • so you will always be returning nil from this method
  • which means you will always get the error above.

To explain the error:

+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Account'

It's not immediately obvious from reading it, but this means that nil is not a legal thing to pass for the managed object context. On first reading, it looks like you're doing entityForName:nil but that isn't the case.

To fix the problem, you will need to provide a valid persistent store coordinator. I have a small article here which explains just how little code you need to set up a core data stack, this may help you.

Solution 2

In my case, I'm using multiple contexts (parent/child) with different concurrency types to improve performance. I have three contexts:

  1. storeContext which is the only context whose persistentStoreCoordinator had been set.
  2. viewContext whose parent is storeContext
  3. backgroundContext whose parent should have been viewContext but I forgot to set backgroundContext.parent = viewContext.

Saving an entity on the backgroundContext produced the same error...

+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name...

...because backgroundContext wasn't a part of the parent/child context chain.

Setting backgroundContext's parent to viewContext established the chain back to the persistent store coordinator and resolved the error.

Share:
32,682
ferrojr
Author by

ferrojr

Updated on May 15, 2020

Comments

  • ferrojr
    ferrojr about 4 years

    I have tried a lot of options, but can't find the solution for this problem. I created a Core Data file and named the entity Account, Created an string attribute called username. Then edited the class of the entity to NSManagedObject, not sure if this is right. Now, the following code is in my LoginViewController:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        ITAppDelegate *appDelegate = (ITAppDelegate *)[[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = appDelegate.managedObjectContext;
    
        Account *newAccount = [NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:context];
        [newAccount setValue:@"Jorge" forKey:@"username"];
        [newAccount setPassword:@"password"];
    
        NSLog(@"username:%@   password: %@", [newAccount username], [newAccount password]);
    
    }
    

    I followed This Tutorial and my code files look like this:

    ITAppDelegate.h

    #import <UIKit/UIKit.h>
    
    @interface ITAppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow *window;
    
    @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
    @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
    @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
    
    @end
    

    ITAppDelegate.m

    #import "ITAppDelegate.h"
    #import "LoginViewController.h"
    
    @implementation ITAppDelegate
    
    @synthesize managedObjectContext = _managedObjectContext;
    @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
    @synthesize managedObjectModel = _managedObjectModel;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary    *)launchOptions
    {
        // Override point for customization after application launch.
        return YES;
    }
    
    #pragma mark - Core Data stack
    
    - (NSManagedObjectContext *)managedObjectContext
    {
    if (_managedObjectContext != nil)
    {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext  setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
    }
    
    
    - (NSManagedObjectModel *)managedObjectModel
    {
    if (_managedObjectModel != nil)
    {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
    }
    
    
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
    if (_persistentStoreCoordinator != nil)
    {
        return _persistentStoreCoordinator;
    }
    
        return _persistentStoreCoordinator;
    }
    
    @end
    

    AccountBase.h

    #import <UIKit/UIKit.h>
    #import <CoreData/CoreData.h>
    
    @interface AccountBase : NSManagedObject
    
    @property (nonatomic, retain) NSString *username;
    
    
    @end
    

    AccountBase.m

    #import "AccountBase.h"
    
    @implementation AccountBase
    
    @dynamic username;
    
    @end
    

    Account.h

    #import "AccountBase.h"
    #import <CoreData/CoreData.h>
    
    @interface Account : AccountBase
    
    @property (nonatomic, assign) NSString *password;
    
    @end
    

    Account.m

    #import "Account.h"
    #import "KeychainHelper.h"
    
    @implementation Account
    
    - (NSString*)password
    {
        if (self.username)
            return [KeychainHelper getPasswordForKey:self.username];
        return nil;
    }
    
    - (void)setPassword:(NSString*)aPassword
    {
        if (self.username)
            [KeychainHelper setPassword:aPassword forKey:self.username];
    }
    
    - (void)prepareForDeletion
    {
        if (self.username)
            [KeychainHelper removePasswordForKey:self.username];
    }
    @end
    

    KeychainHelper.h

    #import <Foundation/Foundation.h>
    
    @interface KeychainHelper : NSObject
    
    + (NSString*)getPasswordForKey:(NSString*)aKey;
    + (void)setPassword:(NSString*)aPassword forKey:(NSString*)aKey;
    + (void)removePasswordForKey:(NSString*)aKey;
    
    @end
    

    KeychainHelper.m

    #import "KeychainHelper.h"
    #import <Security/Security.h>
    
    @interface KeychainHelper ()
    + (NSMutableDictionary*)dictionaryForKey:(NSString*)aKey;
    @end
    
    @implementation KeychainHelper
    
    
    static const NSString *ironTrainers = @"com.domain.myapplication";
    
    + (NSMutableDictionary*)dictionaryForKey:(NSString*)aKey
    {
        NSData *encodedKey = [aKey dataUsingEncoding:NSUTF8StringEncoding];
    
        NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
    
        [searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
        [searchDictionary setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
        [searchDictionary setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
        [searchDictionary setObject:ironTrainers forKey:(__bridge id)kSecAttrService];
    
        return searchDictionary;
    }  
    
    + (NSString*)getPasswordForKey:(NSString*)aKey
    {
        NSString *password = nil;
    
        NSMutableDictionary *searchDictionary = [self dictionaryForKey:aKey];
        [searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
        [searchDictionary setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
    
        CFTypeRef result = NULL;
        BOOL statusCode = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &result);
        if (statusCode == errSecSuccess) {
            NSData *resultData = CFBridgingRelease(result);
            password = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
        }
        return (__bridge NSString *)(result);
    }
    
    + (void)removePasswordForKey:(NSString*)aKey
    {
        NSMutableDictionary *keyDictionary = [self dictionaryForKey:aKey];
        SecItemDelete((__bridge CFDictionaryRef)keyDictionary);
     }
    
    + (void)setPassword:(NSString*)aPassword forKey:(NSString*)aKey
    {
        [KeychainHelper removePasswordForKey:aKey];
    
        NSData *encodedPassword = [aPassword dataUsingEncoding:NSUTF8StringEncoding];
    
        NSMutableDictionary *keyDictionary = [self dictionaryForKey:aKey];
        [keyDictionary setObject:encodedPassword forKey:(__bridge id)kSecValueData];
        SecItemAdd((__bridge CFDictionaryRef)keyDictionary, nil);
    }
    
    @end
    

    Any help appreciated. Thanks.

  • jrturton
    jrturton over 10 years
    The error message states that the managedObjectContext parameter is nil, this is nothing to do with the model
  • ferrojr
    ferrojr over 10 years
    Thanks, it worked for the username. I changed app delegate (please see above), but now Im getting the error: -[NSManagedObject setPassword:]: unrecognized selector sent to instance
  • jrturton
    jrturton over 10 years
    Well, that's a whole different question. That looks like you haven't defined the proper subclass in your data model (there's a "Name" and "Class" box to fill in)
  • ferrojr
    ferrojr over 10 years
    My data model has one attribute which is username. And the entity name is Account, the class that shows there is NSManagedObject, which I edited earlier, not sure if that's right though.
  • jrturton
    jrturton over 10 years
    You have to set the class to Account and generate the Account.h and Account.m files by selecting the entity and choosing editor --> create NSManagedObject subclass..
  • ferrojr
    ferrojr over 10 years
    This is my output: username:Jorge password: <70617373 776f7264> I'm guessing it worked, right?