Using Core Data in a Static Library !

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

CredentialManagerLib_01

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.

CredentialManager_09CredentialManager_10

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

CredentialManagerLib_02 CredentialManagerLib_03 CredentialManagerLib_04 CredentialManagerLib_05

Now drag and drop Credentials.xcdatamodeld to CredentialDepot -> Build Phases ->Compile Sources as shown below

CredentialManagerLib_06 CredentialManagerLib_07

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

CredentialManagerLib_08

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)

CredentialManagerLib_09

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);

}

}

&nbsp;

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

CredentialManagerLib_11 CredentialManagerLib_12 CredentialManagerLib_13

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.

CredentialManagerLib_14

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

Persisting Data Using Core Data

Core Data is basically a model layer technology which helps to build the model layer that represents the state of the app. Core Data can also be used as a persistent technology which helps to persist the state of the model object to the file storage. In this post, we will learn how to use Core Data to persist data, say, username and password given by the user in a persistent storage/database. Here are the prerequisites

  • Should have Xcode 5 running on your Mac
  • Fair knowledge on Xcode/iOS development
  • Should have created any applications using basic UI elements like UILabel, UIButton,etc

Lets get started by creating a Single View Application as shown below

CredentialManager_01

 

Open Main.storyboard and embed the view controller in a navigation controller. Now, drag and drop two UIViews in the storyboard and name it as RegistrationView and LoginView. The first time when the application is launched, we will show the RegistrationView. Once the user enters the credentials and logs in, for subsequent launches, we will show the LoginView. In the RegistrationView, create IBOutlets for the textfields and methods for the buttons as show below

CredentialManager_02

Similarly, In the LoginView, create IBOutlets for the textfields and methods for the buttons as shown below

CredentialManager_03

 

Before we get started with Core Data, lets write some logic to show registration screen on the first launch of the application.We store a flag in NSUserDefaults to indicate whether its a first time launch or not.

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *launchFlag = [userDefaults objectForKey:@"LaunchFlag"];
self.isFirstTimeLaunch = (launchFlag.length) >0 ? YES:NO;

Handle basic operations like showing alert if username & password fields are blank and update the methods which gets called on Register/Login. Now, lets start writing code to persist user credentials using Core Data. Core Data has 3 main components – Managed Object Model – Its a database schema, contains definitions for each of the Entities that you are storing in the database Persistent Store Coordinator – Its a database connection which has the information about name and location of the database Managed Object Context – Its a single instance (in most of the case) which holds all the managed objects

CredentialManager_04

 

A simple way to generate code for Core Data is to create a Master-Detail Application and marking the checkbox when prompted for “Use Core Data”. I don’t know why Apple has provided this option only in Master-Detail Application.

CredentialManager_05 CredentialManager_06 CredentialManager_07

 

Now, use this code in our application to create a data base schema to store the login credentials of the user. Copy the core data code in the Master-Detail Application AppDelegate and paste it in our application CredentialManager AppDelegate. If you try to compile our application, it will throw an error pointing NSPersistentStoreCoordinator & NSManagedObjectModel. Inorder to resolve this error, add CoreData framework in the Build Phases as shown below.

CredentialManager_08

 

Import core Data in AppDelegate #import <CoreData/CoreData.h> This should resolve all our errors !! Right click on the project navigation panel, select Core Data, select DataModel, name the model as “Credentials” and click on create as shown below

CredentialManager_09

 

Once Credentials.xcdatamodeld is created, click on that make following changes

1. Click on Add Entity and name the entity as “UserCredentials”

2. Click on + symbol in the attributes and add attributes username & password, select the type as string. This will be our database schema. Note : You can add more entities and create relationships between entities by using Editor Style (3)

CredentialManager_10

 

Now, go back to AppDelegate and change the name of momd to Credentials and sqlite to CredentialManager.sqlite as shown below

CredentialManager_11

The beauty of Core Data is that we need not have to take pain in creating model classes for the entities in our data base schema. Create a new file -> Core Data -> NSManagedObject subclass , select Credentials, select UserCredentials to create a new modal file. We will call a method when user taps on Register/Login button. This method will have the logic to save or update the credentials entered by the user. I know we should not save the credentials in clear text but lets not worry about the encryption part as this post is more about persisting the data ! 🙂

Paste this code in the method which gets called when user taps Register/Login button

BOOL isSuccess = NO;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate 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];
        if ([[userCredentials.username lowercaseString] isEqualToString:[self.usernameTextField.text lowerca seString]]

        && [[userCredentials.password lowercaseString] isEqualToString:[self.passwordTextField.text lowercaseString]])
        {

            userCredentials.username = self.usernameTextField.text;

            userCredentials.password = self.passwordTextField.text;

            if (![context save:&error]) {

                NSLog(@"Error in saving Credentials: %@", [error localizedDescription]);

            }else{

                NSLog(@"Update Credentials in DB : Username - %@, Password - %@", userCredentials.username, userCredentials.password);

                isSuccess = YES;

            }

        }

        else{

            isSuccess = NO;

        }

    }else{

        UserCredentials *userCredentials = [NSEntityDescription insertNewObjectForEntityForName:@"UserCredentials" inManagedObjectContext:context];

        userCredentials.username = self.usernameTextField.text;

        userCredentials.password = self.passwordTextField.text;

        if (![context save:&error]) {

            NSLog(@"Error in saving Credentials: %@", [error localizedDescription]);

        }else{

            NSLog(@"Insert Credentials in DB : Username - %@, Password - %@", userCredentials.username, userCredentials.password);

            isSuccess = YES;

        }

    }

Here, we are going to rely on the managed object context from AppDelegate. We query the user credentials from the entity we created in our Credentials.xcdatamodel.  With the help of NSFetchRequest & NSEntityDescription, we query if any credentials are saved in the DB. If present, we compare with the values given by the user and display messages based on the result of comparison operation!

CredentialManager_13

 

Hope this post helped you to understand how Core Data can be used to persist data in your application. You can download the source code from this link.