In this post, we will learn how to use Core Data in a static library. After I learnt about the concepts of core data, I decided to create a library to manage my credentials using core data. It may not be straightforward but its not rocket science either 😀
Before we begin with this post, here are the perquisites
- Should have Xcode 5 running on your Mac
- Fair knowledge on Xcode/iOS development
- Should have basic knowledge on core data. Refer this link to get hands on core data !
Here is what we are going to achieve. We will create a static library and understand how we can use core data to store user credentials. Later, we will plug this library in our CredentialManager application created in this post.
Lets get started by creating a static library as shown below
Lets name the Library as “BNRCredentialDepot”.
Since core data uses managed object model (momd) and sqlite to persist the data, moms should be bundled in the library. In order to achieve this, we have to create a bundle and include momd in the bundle.
Add a new DataModel file and create entities and attributes as explained in this post.
We need to embed this data model in a bundle file so that we can package this along with the library.
Add a new target and create a bundle “CredentialDepot”, change the base SDK to Latest iOS SDK as shown below
Now drag and drop Credentials.xcdatamodeld to CredentialDepot -> Build Phases ->Compile Sources as shown below
Expand the Other Frameworks folder in the left pane and delete the duplicate frameworks – Foundation.framework, AppKit.framework. Add CoreData.framework in the build phase
Now, add a new file of type NSObject class and create managed object context, managed object model & persistent store coordinator as shown below (you can copy the same from Appdelegate of CredentialManager app)
Remember, we have embed the Credentials.xcdatamodeld in the CredentialDepot bundle. Hence the path to access this in managed object model should be relative to this bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"CredentialDepot" ofType:@"bundle"]; NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; NSString *modelPath = [bundle pathForResource:@"Credentials" ofType:@"momd"]; NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
Once this is set, we will write 2 methods, one to fetch the user credentials & the other to store the credentials as shown below
-(NSMutableDictionary *)getUserCredentialsFromDB { NSMutableDictionary *userCredentialDict = nil; NSManagedObjectContext *context = [self managedObjectContext]; NSError *error; <span style="line-height: 1.5em;"> // Fetch all the available username/password stored in the database</span> NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"UserCredentials" inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; if ([fetchedObjects count]) { <span style="line-height: 1.5em;"> UserCredentials *userCredentials = [fetchedObjects objectAtIndex:0];</span> userCredentialDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys: userCredentials.username,USERNAME, userCredentials.password,PASSWORD, nil]; <span style="line-height: 1.5em;"> NSLog(LIBRARY_LOGS_BEGIN);</span> NSLog(@"Retried From Database : Username - %@ , Password - %@",userCredentials.username,userCredentials.password); NSLog(LIBRARY_LOGS_END); } return userCredentialDict; }
-(BOOL)storeUserCredentialsFromDB:(NSMutableDictionary *)userCredentialsDict { BOOL isSuccess = NO; NSManagedObjectContext *context = [self managedObjectContext]; NSError *error; // Fetch all the available username/password stored in the database NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"UserCredentials" inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; if ([fetchedObjects count]) { UserCredentials *userCredentials = [fetchedObjects objectAtIndex:0]; @try { //Store Username & Password in the DB userCredentials.username = [userCredentialsDict objectForKey:USERNAME]; userCredentials.password = [userCredentialsDict objectForKey:PASSWORD]; NSLog(LIBRARY_LOGS_BEGIN); if (![context save:&error]) { NSLog(@"Error in saving User Credentials: %@", [error localizedDescription]); }else{ //If the credentials are already preset, update the record isSuccess =YES; NSLog(@"Update User Credentials in DB : Username - %@ , Password - %@",userCredentials.username,userCredentials.password); } NSLog(LIBRARY_LOGS_END); }@catch (NSException *exception) { //Log exception NSLog(LIBRARY_LOGS_BEGIN); NSLog(@"Exception - %@",exception.description); NSLog(LIBRARY_LOGS_END); } }else{ UserCredentials *userCredentials = [NSEntityDescription insertNewObjectForEntityForName:@"UserCredentials" inManagedObjectContext:context]; @try { //Store Username & Password in the DB userCredentials.username = [userCredentialsDict objectForKey:USERNAME]; userCredentials.password = [userCredentialsDict objectForKey:PASSWORD]; NSLog(LIBRARY_LOGS_BEGIN); if (![context save:&error]) { NSLog(@"Error in saving User Credentials: %@", [error localizedDescription]); }else{ //If the credentials are not preset, insert the record isSuccess =YES; NSLog(@"Insert User Credentials in DB : Username - %@ , Password - %@",userCredentials.username,userCredentials.password); } NSLog(LIBRARY_LOGS_END); }@catch (NSException *exception) { //Log exception NSLog(LIBRARY_LOGS_BEGIN); NSLog(@"Exception - %@",exception.description); NSLog(LIBRARY_LOGS_END); } } return isSuccess; }
With our logic to fetch and store the user credentials is written, we have to link the target of the bundle with the application.
Go to Product -> Scheme -> Edit Scheme
Select Build & then select CredentialsDepot , click Add as shown below
Add this code in BNRCredentialDepot class
+(NSMutableDictionary *)getUserCredentials { BNRCredentialManager *credentialManager = [[BNRCredentialManager alloc] init]; return [credentialManager getUserCredentialsFromDB]; }
+(BOOL)storeUserCredentials:(NSMutableDictionary *)userCredentialsDict { BNRCredentialManager *credentialManager = [[BNRCredentialManager alloc] init]; return [credentialManager storeUserCredentialsFromDB:userCredentialsDict]; }
Now, its time to build the library ! Use CMD+B to build the library. It should build without any errors.
Make sure both .a and .bundle files are generated with any errors !
Now, open our CredentialManager application and add libBNRCredentialDepot.a , CredentialsDepot.bundle & BNRCredentialDepot.h in CredentialManager.
All you need to do is call these methods on click of Register & Login buttons respectively !
</pre> -(BOOL)isRegistrationSuccess { NSMutableDictionary *userDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:self.usernameTextFieldForRegistration.text,USERNAME,self.passwordTextFieldForRegistration.text,PASSWORD, nil]; BOOL isSuccess= [BNRCredentialDepot storeUserCredentials:userDict]; return isSuccess; }
<pre>-(BOOL)isLoginSuccess { BOOL isSuccess = NO; NSMutableDictionary *userDict = [BNRCredentialDepot getUserCredentials]; if ([[userDict objectForKey:USERNAME] isEqualToString:self.usernameTextFieldForLogin.text] && [[userDict objectForKey:PASSWORD] isEqualToString:self.passwordTextFieldForLogin.text]) { isSuccess = YES; return isSuccess; } return isSuccess; }
Congratulations ! You have successfully created a library which manages your credentials and you can plug into any application ! 🙂
The source code of the Library can be downloaded from this link
The source code of the CredentialManager which uses the library can be downloaded from this link