Performance issue during insertion of document to collection between Android and iOS

In iOS Reading a JSON file and inserting to Realm database with below piece of code is taking 3236.17 ms . File size is 7.6MB with 21837 records

var config = Realm.Configuration.defaultConfiguration

config.fileURL!.deleteLastPathComponent()

config.fileURL!.appendPathComponent(“drugs”)

config.fileURL!.appendPathExtension(“realm”)

var defaultRealm = try! Realm(configuration: config)

let fileUrl = FileManager.default.epocPrivateDataURL.appendingPathComponent(“pill_propertiesV2.json”)

do {

let data = try Data(contentsOf: fileUrl)

let pillProperties = try JSONDecoder().decode(RLMPillProperties.self, from: data)

try? defaultRealm.write {

realm.add(pillProperties)

}

} catch {

print(“Error reading monograph (error)”)

}

Same operation in Android it is taking 224ms with single API createObjectFromJson .

Any reason why the performance is low in iOS? Is there any such kind of API at RealmSDK level similar to Android one?

I ran your code with 10,000 objects and it took about a second. I am running it on iMac 2017.

I ran this in MacBook Pro (15-inch, 2019) and This is the structure of single record in JSON. Tried the same in multiple machine the result is same.

{
“drugId”: 1713,
“ifile”: “LLY07151”,
“color”: “white”,
“shape”: “”,
“score”: “”,
“clarity”: “cloudy”,
“coating”: “”,
“subshape”: “”,
“subcolor”: “milky white”,
“flavor”: “”,
“imprint1”: “”,
“imprint2”: “”,
“formulation”: “human recombinant 70 units-30 units/mL”,
“drugForm”: “suspension”,
“genericName”: “insulin isophane-insulin regular”
}

Perhaps I am not fully understanding. Let me share my testing code:

For simplicity I have Thing object that just has default values based on your JSON

class Thing: Object {
    @Persisted var drugId = 1713
    @Persisted var ifile = "LLY07151"
    @Persisted var color = "white"
    @Persisted var shape = ""
    @Persisted var score = ""
    @Persisted var clarity = "cloudy"
    @Persisted var coating = ""
    @Persisted var subshape = ""
    @Persisted var subcolor = "milky white"
    @Persisted var flavor = ""
    @Persisted var imprint1 = ""
    @Persisted var imprint2 = ""
    @Persisted var formulation = "human recombinant 70 units-30 units/mL"
    @Persisted var drugForm = "suspension"
    @Persisted var genericName = "insulin isophane-insulin regular"
}

I then have a class var that stores the start time so we can determine the write time

var startTime = Date()

and then a function that creates 10,000 Thing objects and stores them in an array, once completed the startTime is populated and then that array is written to Realm. When the write completes, the endTime is calculated and printed to console.

func writeABunchOfThings() {
    let realm = //your realm

    var myThingArray = [Thing]()

    for _ in 0...9999 { // <-10,000 objects
        let myThing = Thing()
        myThingArray.append(myThing)
    }

    self.startTime = Date()
    realm.writeAsync {
        realm.add(myThingArray)
    } onComplete: { error in
        let elapsed = Date().timeIntervalSince(self.startTime)
        print("done inserting. Elapsed time: \(elapsed)")
    }
}

If I run that code three times, deleting the Realm file in between runs, I get this output in console

done inserting. Elapsed time: 0.5658090114593506
done inserting. Elapsed time: 0.5397540330886841
done inserting. Elapsed time: 0.5507090091705322

I believe the issue with your code is writing each object separately within a tight loop on the main thread- if you need that functionality (which may be more memory safe) this would work noting it’s an asynchronous write.

self.startTime = Date()

realm.writeAsync {
    for _ in 0...9999 {
        realm.create(Thing.self)
    }
} onComplete: { error in
    let elapsed = Date().timeIntervalSince(self.startTime)
    print("done inserting. Elapsed time: \(elapsed)")
}

and the output

done inserting. Elapsed time: 0.9166730642318726
done inserting. Elapsed time: 0.9176139831542969
done inserting. Elapsed time: 0.911078929901123

So not quite as fast but still within reason.

Let me know if that helps.

Jay

I didn’t see anything like realm.writeAsync in SDK… Please confirm.

@Basavaraj_KM1

Confirmed! There are a bunch of ways to code that - I was just using the latest syntax from the SDK but the functionality is consistent.

Add Swift API for asynchronous transactions

    try? realm.writeAsync {
        realm.create(SwiftStringObject.self, value: ["string"])
    } onComplete: { error in
        // optional handling on write complete
    }

    try? realm.beginAsyncWrite {
        realm.create(SwiftStringObject.self, value: ["string"])
        realm.commitAsyncWrite()
    }

    let asyncTransactionId = try? realm.beginAsyncWrite {
        // ...
    }
    try! realm.cancelAsyncWrite(asyncTransactionId)

I am using pod ‘RealmSwift’, ‘~>10’… Any specific version I need to refer here?

@Basavaraj_KM1

Well not really. If you’re using less than 10.26 it would probably be a good idea to update to 10.26

pod update

but if want to use and older SDK you just have to code it without the spiffy new syntax.

Oh, and the new syntax in 10.26 is actually covered in the current documentation (GO REALM TEAM!)

Getting error saying "CocoaPods could not find compatible versions for pod “RealmSwift”:
In Podfile:
RealmSwift (~> 10.26)

Could you please help me on how to get this?

Sure!

If we knew what version of CocoaPods you were using (pod --version) and could see your entire podfile we may be able to spot the issue.

Here’s mine (for macOS)

project 'My Great Realm App'
target 'My Great Realm App' do
  use_frameworks!
  platform :osx, '10.15'
   pod 'RealmSwift', '~>10'
end

There’s an installation guide right on the Realm site as well for reference

Thanks Jay.

Started working after pod update. But I didn’t seen any difference in performance with writeAsync and write. Still getting same results like as mentioned in the beginning

@Basavaraj_KM1

Well, it’s working great for me. I would propose it’s time for a split-half search to eliminate some variables. Please create a brand new macOS XCode project using my podfile from above (changing the project name)

Then copy and paste my Thing object from above and then the code (func writeABunchOfThings) in the new project. I would add a button to run the code itself.

Run the project.

If it works and behaves correctly, something in your original project is causing the issue

If it does not work, then you’ve got an environment issue; maybe a config issue or some other issue - possibly event corrupted files or a hardware problem.

I have followed those steps on three different Macs; a 2019 iMac, 2017 iMac and a 2013 Macbook Pro 15". The results are roughly the same.

Here jsonObject we are getting array of dictionary object. Each dictionary represents one record in RLMIngredient collection.

With this statement realm.create([RLMIngredient].self, value: jsonObject, update: .modified) I am getting compiler error.

What is the procedure to insert array of json object to realm here other than for loop. Below snippet is not working

if let jsonObject = try? JSONSerialization.jsonObject(
                    with: data,
                    options: []
                ) as? [[String: AnyObject]] {
                    try? realm.writeAsync({
                        realm.create(RLMIngredient.self, value: jsonObject, update: .modified)
                    }, onComplete: { error in
                        print(error)
                        print("Realm onComplete - Time taken to Read JSON file and insert ingredients \(Date().timeIntervalSince(start) * 1000)")
                    })
                }

My apologies but I am struggling trying to understand what you’re attempting to do with that code.

The .create function creates a single realm object so this

.create([RLMIngredient].self

won’t work as [].self is an array and not a Realm object. It’s unclear what the purpose of that would be even if it did work.

Why? Why do you need an array of dictionaries for? Where does data come from? Is it encoded? Why not just pass data to a Realm object when it’s instantiated, let the object decode it and assign properties and then write it out to Realm?


Your new question is

And the answer is you cannot insert and array of json objects to Realm. You can only insert Realm objects into Realm.


I feel the thread is getting off topic.

The initial issue was Realm write performance. Using my examples above, we’re not able to replicate that issue which would indicate the problem lies elsewhere in your code that was not included.

Working with JSONSerialization, arrays etc are off topic for this discussion and to prevent the thread from being a mile-long, it should probably be posed as another question (it’s really a separate topic)

Happy to help but it’s important to keep threads on a single topic so it may help future readers that are looking for help about this particular subject matter.

So, post a separate question with the code you’re stuck on an we’ll take a look!

A post was split to a new topic: Is there any way to get updates for topics