I've been using realm in my app for a while. now I've added Share Extension to the app and I noticed that I need to define realm default file path to group subdirectory so I can access to the same database from both App and Extension. I searched several times and I found the best solution in here and with the help of here.
Here is my AppDelegate File
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// migrateRealm()
// configRealm()
return true
}
private func configRealm() {
let fileURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.com.hjri.khandehland")!
.appendingPathComponent("Library/Caches/default.realm")
let config = Realm.Configuration(fileURL: fileURL)
Realm.Configuration.defaultConfiguration = config
}
private func migrateRealm() {
let fileManager = FileManager.default
let originalDefaultRealmURL = Realm.Configuration.defaultConfiguration.fileURL
//Cache original realm path (documents directory)
let originalDefaultRealmPath = originalDefaultRealmURL?.path
//Generate new realm path based on app group
let realmURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.com.hjri.khandehland")!
.appendingPathComponent("Library/Caches/default.realm")
let realmPath = realmURL.path
if originalDefaultRealmPath != nil {
//Moves the realm to the new location if it hasn't been done previously
if (fileManager.fileExists(atPath: originalDefaultRealmPath!) && !fileManager.fileExists(atPath: realmPath)) {
print("***** FILE EXISTS AND MOVING")
do {
try fileManager.moveItem(atPath: originalDefaultRealmPath!, toPath: realmPath)
} catch {
print("***** REALM FILE PATH MIGRATION ERROR *****")
}
} else {
print ("***** FILE DOES NOT EXIST *****")
}
}
//Set the realm path to the new directory
Realm.Configuration.defaultConfiguration.fileURL = realmURL
}
Also, my model classes are
class User: Object {
@objc dynamic var username: String = ""
@objc dynamic var email: String = ""
@objc dynamic var password: String = ""
@objc dynamic var accessToken: String = ""
@objc dynamic var refreshToken: String = ""
}
class Message: Object {
@objc dynamic var id: Int = 0
@objc dynamic var content: String = ""
@objc dynamic var submitDate: String = ""
@objc dynamic var submitDateEpoch: Double = 0
@objc dynamic var favoriteCount:Int = 0
@objc dynamic var isFavorite: Bool = false
@objc dynamic var needsSync: Bool = false
@objc dynamic var syncDetails: String = ""
@objc dynamic var isNew: Bool = true
}
For testing my app first I work with models using the default realm file path, and then I uncomment one of migrateRealm()
or configRealm()
methods which none of them won't work correctly, after uncommenting the first one, I can log in (create user instance) and after relaunch the app, it reads the User data correctly, but messages won't appear in the app at all which means there's a problem in reading/writing them, when uncommenting the configRealm
which should just change the realm path and don't migrate the old data, the app still shows me the old messages, but it doesn't load the user, whcih means my login state is gone after relaunch, and also if I update a message property which makes the realm change a message model property, it throws this error and the app crashes:
* Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.' * First throw call stack
This error says that I'm doing the update operation outside of the realm write closure, but my code worked perfectly before changing the path, and also I do write the update commands inside the write block as you can see here, this is my message update method
func update(content: String? = nil, isFavorite: Bool? = nil, needsSync: Bool? = nil, syncDetails: String? = nil, favoriteCount: Int? = nil) {
let realm = try! Realm()
try! realm.write {
self.content = content ?? self.content
self.isFavorite = isFavorite ?? self.isFavorite
self.needsSync = needsSync ?? self.needsSync
self.syncDetails = syncDetails ?? self.syncDetails
self.favoriteCount = favoriteCount ?? self.favoriteCount
}
}
I don't know whether I made a mistake or it's a realm problem or XCode problem (I use the beta version) Very Confused at this moment
I use Realm 3.7.5 XCode 10 Beta
I made it work by setting my custom configuration to Realm instance everytime I call it, I made it easier with making this computed property and calling it anytime I wanted to use Realm
static var realmInstance: Realm {
let fileURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier)!.appendingPathComponent(databaseFileName)
var config = Realm.Configuration(fileURL: fileURL)
return try! Realm(configuration: config)
}