NSBatchUpdateRequest in Swift and its advantage

Rajan Maheshwari
Dec 2, 2016 · 5 min read

In this blog, we will learn to implement NSBatchUpdateRequest of Core Data introduced in iOS 8 in Swift 3.x version. We will also keep an eye on the memory usage and execution time. I have also made a video tutorial on this topic. Do check the link at the end of the blog.

From iOS 8 and above and OS X Yosemite and above, we can directly interact with persistent store and update an attribute. This is batch update according to Apple. So without wasting further time, lets dive into the Xcode for the code section.

Create a New Project and make sure Use Core Data checkbox is checked. After creating the project, go to .xcdatamodeld file and create an entity called Student. We will also create its two attributes, studentName and studentRollNo.

There is a weird error in Swift 3.x whenever we try to create NSManagedObject classes from the editor — filename with same name redeclaration. So, in order to fix that, choose the entity and in the Data Inspector section go to Class. Change the Module to Current Product Module and Codegen to Manual/None as shown in the picture below.

Then finally click on Editor and then click on Create NSManagedObject Subclass…

So we have our model subclasses ready. Here they look like.

Student+CoreDataClass.swift

import Foundation
import CoreData
public class Student: NSManagedObject {}

Student+CoreDataProperties.swift

import Foundation
import CoreData
extension Student {@nonobjc public class func fetchRequest() -> NSFetchRequest<Student{
return NSFetchRequest<Student>(entityName: "Student");
}
@NSManaged public var studentName: String?
@NSManaged public var studentRollNo: Int64
}

Now moving on to our view controller, we will add 50,000 student records in our Core Data. Lets go to out ViewController.swift. We will require an AppDelegate shared object and the context for Core Data.

import UIKit
import CoreData
class ViewController: UIViewController {let appDelegate = UIApplication.shared.delegate as! AppDelegatelet context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContextoverride func viewDidLoad() {
super.viewDidLoad()
}

Function to insert 50,000 records. I am inserting the student name as the current date and roll number as the serial value of for loop from 1 to 50,000.

func insert() {

for i in 1…50000 {

let student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: context) as! Student
student.studentName = "\(Date())"
student.studentRollNo = Int64(i)
}
appDelegate.saveContext()
}

We will call this insert function in viewDidLoad

override func viewDidLoad() {
super.viewDidLoad()
//It will print the actual storage of sqlite file generated by core data print(appDelegate.persistentContainer.persistentStoreDescriptions) insert()
}

print(appDelegate.persistentContainer.persistentStoreDescriptions) will print the actual path where the .sqlite file is.

It will print a path like:

/Users/rajan/Library/Developer/CoreSimulator/Devices/B8BD5017–4BD0–47F7-B840-BE9464817392/data/Containers/Data/Application/2B0E7289–1A4C-4F78-AD00–72285B3FA5F9/Library/Application%20Support/yourProjectName.sqlite

We can navigate to this path and can use a simple Add-ons of Firefox called SQLite Manager. You can download it from Add- ons of Firefox.

Click on SQLite Manager, select connect to database (yellow folder icon) and navigate to the path printed on your console to your .sqlite file.

Click on the Yellow Folder Icon

Navigate to the .sqlite file path

CoreDataBatchRequest is the name of my project

Open the .sqlite file

You will see 50,000 entries of students. Now we have inserted the records, we will comment out the insert function and proceed with update now.

We will create a function to get the time in milliseconds in ViewController.swift

func currentTimeMillis() -> Int64 {
return Int64(Date().timeIntervalSince1970 * 1000)
}

Now we will create two update methods, one by batch and one by the old traditional approach.

//Traditional Approach
func update() {
let request:NSFetchRequest<Student> = Student.fetchRequest()
do {
let start = currentTimeMillis()
let searchResults = try context.fetch(request)
for student in searchResults {
student.studentName = "Rajan"
}
appDelegate.saveContext() print("Difference is \(currentTimeMillis() — start)")
} catch {
}
}
//Using NSBatchUpdateRequest
func updateWithBatch() {
let request = NSBatchUpdateRequest(entityName: "Student")
request.propertiesToUpdate = ["studentName":"Rajan Maheshwari"]
request.resultType = .updatedObjectsCountResultType

do {
let start = currentTimeMillis()
let result = try context.execute(request) as! NSBatchUpdateResult
//Will print the number of rows affected/updated
print(result.result!)
print("Success")
print("Difference is \(currentTimeMillis() — start)")
}catch {
}
}

Now we will first call the update function in viewDidLoad . This will change the 50,000 students name to Rajan . Make sure to comment insert function.

override func viewDidLoad() {
super.viewDidLoad()
//insert()
update()
}

So here are the results:

It took 53.8 MB and 774 milliseconds. Also the DB is updated

Now let’s do that with NSBatchUpdateRequest by calling updateWithBatch . Comment out the update and insert method. It will update the name from Rajan to Rajan Maheshwari

override func viewDidLoad() {
super.viewDidLoad()
//insert()
//update()
updateWithBatch()
}

And the results are:

It took around 22.4 MB and just 91 milliseconds. Also the DB is updated.

Can we see the difference.

Traditional ApproachMemory - 53.8 MB
Execution Time - 774 milliseconds
While using NSBatchUpdateRequestMemory - 22.4 MB
Execution Time - 91 milliseconds
This is the advantage of using NSBatchUpdateRequest

Traditional approach requires loading of every record into memory, update the record, and send the changes to the persistent store. So, it will be slow and consume a lot of memory.

In case of NSBatchUpdateRequest , it directly talks to the persistent store and modifies the attribute then and there. Apple recommends to only use this feature if the traditional approach is too resource or time intensive.

So, batch requests are easy to implement and are useful when we are updating hundreds and thousands of attributes instantly.

Video tutorial:

For my further video tutorials on iOS Development, please check my youtube channel.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade