iOS interview questions (7)

Core Data & Concurrency
Let's suppose , you're developing a small or simple application, then you probably don't see the benefit of running Core Data operations in the background. However, what would happen if you imported…

iOS 2018 Series: Cracking iOS interview or Become iOS expert (7)

Chapter 7: Core Data & Concurrency

Let’s suppose , you’re developing a small or simple application, then you probably don’t see the benefit of running Core Data operations in the background. However, what would happen if you imported hundreds or thousands of records on the main thread during the first launch of your application? The consequences could be dramatic. We have face all these kind of issue in real programming world when worked with core data so lets discuss the solution..

Basics:

Core Data is a framework that you use to manage the model layer objects in your application

Concurrency is the ability to work with the data on more than one queue at the same time.

Steps:

* Initializing the Core Data Stack

  1. Create Manage object model instance using model file
  2. Create persistent store coordinator using Manage object model instance
  3. Add persistent store in PSC instance using SQLite or other type and file path for SQLite dB
  4. Create Managed Object Context with concurrency type — NSMainQueueConcurrencyType and NSPrivateQueueConcurrencyType.
  5. Set persistent store coordinator in Manage object model instance.

* Creating and Saving Managed Objects

  1. Create entity using NSEntityDescription entity
  2. Create instance of row which need to be inserted which will be Managed Object instance using entity and MOC.
  3. set Value in MO instance
  4. Save MO entry in core data — context. Save()

* Fetching Objects

  1. Prepare the request of type NSFetchRequest for the entity

let request = NSFetchRequest<NSFetchRequestResult>(entityName: “Users”)

2. Fetch the result from context in the form of array of [NSManagedObject]

let result = try context.fetch(request)

3. Iterate through an array to get value for the specific key

for data in result as! [NSManagedObject]

Core Data Stack Setup — The Easy Way (NSPersistentContainer)

Setting up a Core Data stack takes some work. A typical setup needs a number of steps:

  • Load the managed object model from the application bundle
  • Create a persistent store coordinator with the model
  • Ask the coordinator to load a persistent store
  • Create one or more managed object contexts

Starting with iOS 10 the NSPersistentContainer removes most of this boilerplate code. A container that encapsulates the Core Data stack in your application.

If you adopt some simple naming conventions and the default store location the setup of a core data stack can be as simple as this:

container = NSPersistentContainer(name: "storeName")
container.loadPersistentStores { (storeDescription, error) in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}

you need to set the store description before you load the store.

Persistent Store Descriptor

A persistent container collects the settings for a persistent store into a persistent store descriptor. To override the defaults create a new descriptor before loading the store. You create a persistent store descriptor with the URL for the store.The most commonly used configuration settings:

  • type: String constant specifying the store type (default is NSSQLLiteStoreType).
  • isReadOnly: Bool set to true for a read only store (default is false).
  • shouldAddStoreAsynchronously: Bool set to true to add a store to the coordinator asynchronously on a background thread. The default is false which adds the store synchronously on the calling thread.
  • shouldInferMappingAutomatically: Bool if the flag shouldMigrateStoreAutomatically is true try to infer a mapping model when migrating. Default is true.
  • shouldMigrateStoreAutomatically: Bool migrate the store automatically. Default is true.

So to create a read-only persistent store that loads asynchronously on a background thread (and by default that migrates automatically):

let url = NSPersistentContainer.defaultDirectoryURL()
let description = NSPersistentStoreDescription(url: url)
description.shouldAddStoreAsynchronously = true
description.isReadOnly = true
container.persistentStoreDescriptions = [description]

Getting the Main View Context

The persistent container has a convenient read-only property named viewContext to get the managed object context for the main queue. As the name suggests this is the context you should be using when working with your user interface.

container.viewContext.perform({
// ...
})

Performing a Background Task

To avoid blocking the user interface you should not use the main view context for time consuming tasks. Create a private managed object context and execute the task in the background. The persistent container has a convenience method that takes care of creating a temporary private context for you and takes a block to execute:

container.performBackgroundTask({ (context) in 
// ... do some task on the context
  // save the context
do {
try context.save()
} catch {
// handle error
}
})

Getting a Private Context

You can also just get a new private context to use anyway you see fit:

let context = persistentContainer.newBackgroundContext()
context.perform({
// ...
})

Core Data, Multithreading, and the Main Thread

Working with Core Data on multiple threads is actually very simple from a theoretical point of view. NSManagedObject, NSManagedObjectContext, and NSPersistentStoreCoordinator aren't thread safe.

Thread confinement: Create a managed object context for every thread that interacts with Core Data. NSManagedObjectContext class isn't thread safe.

So far, we’ve learned that you need multiple managed object contexts if you perform Core Data operations on multiple threads. The caveat, however, is that managed object contexts are unaware of each others existence. Changes made to a managed object in one managed object context are not automatically propagated to other managed object contexts. How do we solve this problem?

There are two popular strategies that Apple recommends, notifications and parent-child managed object contexts.

  1. Notifications

Managed object context posts three types of notifications:

  • NSManagedObjectContextObjectsDidChangeNotification: This notification is posted when one of the managed objects of the managed object context has changed.
  • NSManagedObjectContextWillSaveNotification: This notification is posted before the managed object context performs a save operation.
  • NSManagedObjectContextDidSaveNotification: This notification is posted after the managed object context performs a save operation.

When a managed object context saves its changes to a persistent store, via the persistent store coordinator, other managed object contexts may want to know about those changes.

2. Parent/Child Managed Object Contexts

Creating a child managed object context is only slightly different from what we’ve seen so far. We initialize a child managed object context by invoking init(concurrencyType:). The concurrency type the initializer accepts defines the managed object context's threading model. Let's look at each concurrency type.

  • MainQueueConcurrencyType: The managed object context is only accessible from the main thread. An exception is thrown if you try to access it from any other thread.
  • PrivateQueueConcurrencyType: When creating a managed object context with a concurrency type of PrivateQueueConcurrencyType, the managed object context is associated with a private queue and it can only be accessed from that private queue.
  • ConfinementConcurrencyType: This is the concurrency type that corresponds with the thread confinement concept we explored earlier. If you create a managed object context using init(), the concurrency type of that managed object context is ConfinementConcurrencyType. Apple has deprecated this concurrency type as of iOS 9. This also means that init() is deprecated as of iOS 9.

There are two key methods that were added to the Core Data framework when Apple introduced parent/child managed object contexts, performBlock(_:) and performBlockAndWait(_:). Both methods will make your life much easier. When you call performBlock(_:) on a managed object context and pass in a block of code to execute, Core Data makes sure that the block is executed on the correct thread. In the case of the PrivateQueueConcurrencyType concurrency type, this means that the block is executed on the private queue of that managed object context.

For more details — https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html#//apple_ref/doc/uid/TP40001075-CH24-SW1

Bonus time: @IBDesignable and @IBInspectable

IBDesignable

IBDesignable attribute will identify the UIView or the elements inherited from UIView — Eg: UIButton, UIImageView, UILabel etc..

For example , I created a Custom UIButton class,

@IBDesignable
open class MYHighLightedButton: UIButton {
public override init(frame: CGRect) {
super.init(frame: frame)
setTitle("MyTitle", for: .normal)
setTitleColor(UIColor.blue, for: .normal)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

You can add custom view in your xib , In the Interface Builder (`StoryBoard`) drag and drop a UIButton. Go to the `Show the Identity Inspector` and set the Class and Module as MYHighLightedButton.

IBInspectable

Lets add some custom properties our button. 🙌 🙌

For this we have to use IBInspectable attribute. Let’s see how we can add them.

@IBInspectable
public var cornerRadius: CGFloat = 2.0 {
didSet {
self.layer.cornerRadius = self.cornerRadius
}
}

This will add the corner_radius property to your button. You will be able to see them in the Attributes Inspector .

How to read only a few attributes of an entity?

We can use NSFetchRequest class’s property “setPropertiesToFetch”. We can pass a array of properties in string format in setPropertiesToFetch method.

Core Data vs Sqlite?

Core Data Works on object graph management. Operates on objects stored in memory. Can create millions of new objects in-memory very quickly.

Sqlite Works on table structure with sql queries. Operates on data stored on disk. Can be slow to create millions of new rows, as it is a disk I/O operation & Have keys like primary, composite keys.

Knowledge is power!