Swift – 4 – Core Data – Part 2 Creating a simple app

Part 1 here.

Hello everyone.

In this tutorial we will create an app using core data and swift 4. We will be able to insert, update, delete & fetch person records in core data. Person will have two attributes name and ssn (social security number). We will also learn how to use predicates to perform a conditional operation (kind of filter or where clause in sqlite). This is the link to source code.

Please support us by comment, like and share our facebook page.

Have a ☕ and start code –

Lets create new project like attached screen shot:

Give it a name “PersonData”, choose language “Swift” and check “Use Core Data” & “Include Unit Tests (yeah we will unit test also :))”  :

click on next.

Now look at your project structure. It have a file named “PersonData.xcdatamodeld”.

So what is “PersonData.xcdatamodeld” ?

In above steps when we start a new project in Xcode and open the template selection dialog, select the Use Core Data checkbox, A source file for the Core Data model is created as part of the template. That source file will have the extension .xcdatamodeld. This is a xml file. Select that file in the navigator area to display the Core Data model editor. The file can be opened and viewed also using by clicking “show package content”.

Double click on content, It’s not more than a xml file :

You can use the visual editor of  “xcdatamodeled” for creating “Entities”, “attributes of entities”, “Fetch Requests”, “Configurations” and “Relationship”. This file is very important when we perform core data migrations.

Now lets dig in “AppDelegate.swift” file and discuss about code. The AppDelegate file contains application life cycle methods and code stubs related to core data. We are interested about core data stuff only. It contains:

  1. Import CoreData framework
  2. Initialize NSPersistentContainer class and thus core data stack objects (Managed object Model, PersistentStoreCoordinator, Managed Object Context).
  3. A method named saveContext(). It saves managed object models in to store.
  4. Application life cycle method named applicationWillTerminate calls saveContext() also to save data in store when app is about to terminate.

The below code creates a NSPersistentContainer object.  An instance of NSPersistentContainer includes all objects needed to represent a functioning Core Data stack.

NSPersistentContainer

A container that encapsulates the Core Data stack (Heart of the core data) in your application. NSPersistentContainer simplifies the creation/initialization and management of the Core Data stack by handling the creation of below objects:

  • managed object model (NSManagedObjectModel),
  • persistent store coordinator (NSPersistentStoreCoordinator),
  • and the managed object context (NSManagedObjectContext).

Below code

Initializes a persistent container with the given name (PersonData).

Parameters – name – The name of the NSPersistentContainer object.

Return Value – A persistent container initialized with the given name.

When persistent container have been initialized,  we need to execute

to instruct the container to load the persistent stores and complete the creation of the Core Data stack.

Once the completion handler has fired, the stack is fully initialized and is ready for use.

storeDescription

As the name of the class suggests, NSPersistentStoreDescription class encapsulates the information and configuration to add a persistent store to the persistent store coordinator. In other words, it describes a persistent store.

Error

If there is an error in the loading of the persistent stores, the NSError value will be populated.

fatalError()

Fatal Error will causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

Check the error message to determine what the actual problem was. Typical reasons for an error here include:

  • The parent directory does not exist, cannot be created, or disallows writing.
  • The persistent store is not accessible, due to permissions or data protection when the device is locked.
  • The device is out of space.
  • The store could not be migrated to the current model version – very important one, this error will give you some clue about the failure of core data migration process, a developer can get help in his debugging process of core data migration failure.

Why we have used lazy var here?

This is  Lazy initialization of persistent container class. Lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive tasks like initializing below objects

  • managed object model (NSManagedObjectModel),
  • persistent store coordinator (NSPersistentStoreCoordinator),
  • and the managed object context (NSManagedObjectContext).

until the first time persistent container is needed.

I must say to understand NSPersistentContainer class please have a look at documentation of NSPersistentContainer. Double click on NSPersistentContainer class in your xcode project, then click jump to definition. You will understand why it is backbone of core data functioning.

Lets’s see what we have more in AppDelegate file.

saveContext()

In below code we are calling persistent container’s view context object. This view context is name of property of type ManagedObjectContext class.

By default view context is the main context. The default concurrency type is MainQueueConcurrencyType.  In Core Data, the managed object context can be used with two concurrency patterns, defined by NSMainQueueConcurrencyType and NSPrivateQueueConcurrencyType.

NSMainQueueConcurrencyType is specifically for use with your application interface and can only be used on the main queue of an application.

The NSPrivateQueueConcurrencyType configuration creates its own queue for background tasks.

There are a lot to discuss about concurrency may be some time letter :).

context.hasChanges

hasChanges method looks for the changes made on managed object model. If there is any change we call save() method of context object to finally save our data. save() method can throw error so we must handle these errors. Discussion on it will make more sense when we will write some code for insert, update and delete.

Last remaining thing in AppDelegate file is:

applicationWillTerminate

applicationWillTerminate is the delegate method of protocol named – UIApplicationDelegate. This delegate method is called when iOS app is about to (see it’s name willTerminate) terminate. This is the best place to save uncommitted changes of managed object context’s managed object models.

Till now we see that Xcode have done basic set up for us which includes creating the xcdatamodeled and important code stubs related to initializing core data stack in AppDelegate file.

Let’s say we want to create a Person entity and create two attributes name and ssn. Attached are the screen shots how we can do it:

click on Add Entity button at the bottom.

Enter entity name as Person.

Add attribute like name and ssn.

This is how visual editor should look like.

After setting up entities and attributes we should create NSManagedObject subclass. There are three ways :

  • Manual/None (i will cover)
  • Class Definition(Default, very easy you can learn on your own)
  • Category/Extension

Manual/None

Choose “Create NSManagedObject subclass” from top editor menu.

Click next

Click next

Click create

Finally we generated the code manually.

Xcode creates and saves files named Person+CoreDataClass and Person+CoreDataProperties for each of your selected entity in the selected location. Person+CoreDataClass implements the NSManagedObjectsubclass.

Person+CoreDataProperties implements a Person+CoreDataClass extension (for Swift apps) or category (for Objective-C apps).

Person – NSManagedObject subclass

Extension on Person class

Extension contains fetch request method, which is a class method. So you do not need to create instance of Person. You can directly call Person.fetchRequest().

This extension also contain properties/attributes with @NSManaged keyword prefix to it. @NSManaged attribute to inform the Swift compiler that Core Data provides the storage and implementation of a declaration at runtime. In the simplest terms this keyword tells compiler there will be some value at run time. @NSManaged is some what same like @dynamic in objective c.

Have a coffee now ☕

Let’s set up the UI in storyboard. I have embed UIViewController in UINavigation Controller. I have added bar button items toward left and right of navigation item. UIViewController contains a UITableView.  Final set up looks like this:

Next, Connect IBAction for Delete By SSN and Add (+).

Set up should be as per attached screen shot:

Make sure you have also set tableView outlet as well as set datasource and delegate methods of the table view.

We should register cell in table view for that change viewDidLoad method as below :

create a stored property, array. This will work for displaying data in table view. It will contain person objects.

Add name function will be called when user will click on top right add (+) button. Change addName: method as per below:

Insert

Below save method contains logic of saving data in core data. Please go through well commented code below:

instead of creating a person object the way below

we can use simpler way to create a person object:

Implement table view delegate methods:

 

let’s run code to see what we have developed till now. We have learned about how can we insert record in core data. We must remember below code for insert :

 

Lets implement didSelectRow delegate method of the table view. When user will click on the row we will show user a alert with options update, delete and cancel.

Let us implement update method as follows:

Finally let’s implement Delete also.

Run project by now we should be able to insert, update and delete entities.

This is how it should look now:

Fetch

So fetching involves init a fetch request for entity name “Person” here,  pass this fetch request to fetch method. The fetch request will return array of person objects [Person].

We can create fetch request this way also:

call fetchAllPerson() function from viewWillAppear as :

After running it we should be now able to fetch person records.

Passing condition/predicate in fetch request

Let’s say we want to delete person by ssn. We can pass ssn in fetch request with the predicate.

Run code it should now look like this:

Sourcecode

In next part we will write some unit test cases also :).

Happy Coding!!

Please support us by comment, like and share our facebook page.

Thanks.