In swift, is there a way to add new data from second Realm bundle, without overwriting any existing data in the current Default Realm?

馋奶兔 提交于 2021-02-16 15:26:30

问题


Upon initial load of the app, the Bundled Realm (Realm1) is copied to the documents folder. Now that the bundled realm is set as the default realm, I am able update the bool property so that the table view can show marked and unmarked cells. However I am looking for a way to bundle a second realm (Realm2) with a later update, that will add new data to the existing default realm, but without overwriting the current default realm. I am currently working in swift 5 and Xcode 11.1, if that is helpful.

So far the only thing that I can think of is adding block of code to add new entries to the default realm. First the view will check to see what the count is of the realm, and if the count is the same as the original bundle, then it will add new data, if the count is equal to the initial bundle plus the new entries, then it will not add the new data again. I was hoping for a simpler solution that is cleaner in my opinion.

Ideally the end result would be a way to update the existing default realm, without overwriting the already edited content. Although I am rather new to using realm, any help in pointing me in the right direction for a solution would be greatly appreciated. Thanks.

Attached below is the current code I have implemented to load the default realm from the bundle.



    let bundlePath = Bundle.main.path(forResource: "preloadedData", ofType: "realm")!
            let defaultPath = Realm.Configuration.defaultConfiguration.fileURL!.path
            let fileManager = FileManager.default

    //        Copy Realm on initial launch
            if !fileManager.fileExists(atPath: defaultPath){
                do {
                    try fileManager.copyItem(atPath: bundlePath, toPath: defaultPath)
                    print("Realm was copied")
                } catch {
                    print("Realm was not coppied \(error)")
                }
            }
            return true


回答1:


Once you've created your default Realm on disk, if you want to read data from the bundled one, here's the code

    let config = Realm.Configuration(
        // Get the URL to the bundled file
        fileURL: Bundle.main.url(forResource: "MyBundledData", withExtension: "realm"),
        // Open the file in read-only mode as application bundles are not writeable
        readOnly: true)
    let realm = try! Realm(configuration: config)

and once you've read the data, you can switch back

var config = Realm.Configuration()
config.fileURL = config.fileURL!.deletingLastPathComponent().appendingPathComponent("\(some_realm_name).realm")
Realm.Configuration.defaultConfiguration = config

as far as not overwriting, ensure your objects use a unique primary key and when they are written, nothing will be overwritten as objects will a unique primary key will be added instead of overwriting.

class MyClass: Object {
    @objc dynamic var my_primary_id = NSUUID().uuidString



回答2:


I am adding an additional answer that's somewhat related to the first but also stands on it's own.

In a nutshell, once Realm connects to a data source, it will continue to use that data source as long as the objects are not released, even if the actual file is deleted.

The way around that is to encapsulate the Realm calls into an autorelease pool so that those objects can be released when the Realm is deleted.

Here’s an example:

This function adds a GameData object to the default.realm file.

func addAnObject() {
   autoreleasepool {
      let realm = try! Realm()
      let testData = GameData()
      testData.Scenario = "This is my scenario"
      testData.Id = 1
      try! realm.write {
         realm.add(testData)
      }
   }
}

At this point, if you run the addAnObject code, your file will have a GameData object.

GameData {
    Id = 1;
    GameDate = (null);
    Scenario = This is my scenario;
    GameStarted = 0;
}

Then a function that delete’s the old realm, and copies the bundled realm to it’s place. This works because all of the interaction with Realm was enclosed in an autorelease pool so the objects can be released.

func createDefaultRealm() {
   let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
   let defaultParentURL = defaultURL.deletingLastPathComponent()

   if let bundledRealmURL = self.bundleURL("default") {
      do {
         try FileManager.default.removeItem(at: defaultURL)
         try FileManager.default.copyItem(at: bundledRealmURL, to: defaultURL)
      } catch let error as NSError {
          print(error.localizedDescription)
          return
      }
   }

   let migrationBlock : MigrationBlock = { migration, oldSchemaVersion in
      //handle migration
   }

   Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 18, migrationBlock: migrationBlock)

   print("Your default realm objects: \(try! Realm().objects(GameData.self))")
}

func bundleURL(_ name: String) -> URL? {
    return Bundle.main.url(forResource: name, withExtension: "realm")
}

and please note that if you access Realm inside the class but outside an autorelease pool, Realm will refuse to 'let go' of it's objects.

Do do NOT do this!!

class ViewController: UIViewController {
    var realm = try! Realm()


来源:https://stackoverflow.com/questions/58417998/in-swift-is-there-a-way-to-add-new-data-from-second-realm-bundle-without-overw

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!