I have a local Realm database, full of 160000 row. I want to copy it to the local path for Realm to be able to use it as official DB, and sync it online. (so my empty Db will be synced). Can I do that? (at this time, doesn't work because it copy the Db in local folder, but not in the user specific folder)
func loginCompletedA(user: SyncUser) {
let realmURL = URL(string: “realm://xxx.compute-1.amazonaws.com:9080/beerUsers”)!
var configuration = Realm.Configuration.defaultConfiguration
configuration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmURL)
let defaultURL = configuration.fileURL!
//let defaultParentURL = defaultURL.deletingLastPathComponent()
if let v0URL = Bundle.main.url(forResource: “default”, withExtension: “realm”){
do {
//if !ifNotExists {
try FileManager.default.removeItem(at: defaultURL)
//}
try FileManager.default.copyItem(at: v0URL, to: defaultURL)
} catch {}
do {
try FileManager.default.copyItem(at: v0URL, to: defaultURL)
} catch {}
}else{
}
let realm = try! Realm(configuration: configuration)
let session = user.session(for: realmURL)!
downloadToken = session.addProgressNotification(for: .download, mode: .reportIndefinitely) {
print(“download progress: \($0)“) // This is never called.
}
uploadToken = session.addProgressNotification(for: .upload, mode: .reportIndefinitely) {
print(“upload progress: \($0)“) // This is never called.
}
}
Just to confirm what I think you're asking. You're pre-bundling a Realm database file, containing 160,000 rows of data along with your app. When a new user logs into the app, the data is then synchronized with their account.
Unsynchronized Realm files and synchronized Realm files are two different file formats, so it's not possible to convert one file to another. Copying a pre-bundled offline Realm to a user-controlled directory and then trying to apply a syncConfiguration object won't do anything.
The easiest solution to this is to make a new synchronized Realm, and then copy the data from the pre-bundled Realm into the synchronized Realm the first time the app launches.
let bundledRealmURL = Bundle.main.url(forResource: “default”, withExtension: “realm”)!
let localConfiguration = Realm.Configuration()
configuration.readOnly = true
configuration.fileURL = bundledRealmURL
let localRealm = try! Realm(configuration: configuration)
let syncConfiguration = Realm.Configuration()
syncConfiguration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmURL)
let syncRealm = try! Realm(configuration: configuration)
let myObjects = localRealm.objects(MyObject.self)
try! syncRealm.write {
for myObject in myObjects {
let newObject = MyObject(value: myObject)
syncRealm.add(newObject)
}
}
We're exploring ways to make it easier to 'prefill' synchronized Realms for a future release of Realm. :)
Related
I am trying to implement a function to backup realm db to iCloud
https://medium.com/swlh/backup-your-applications-data-with-icloud-and-track-progress-48a00ebd2891
I finished the setting by referring to the site above.
And I entered the code according to the method of the site, but with no success.
So I tried again like below.
Still I can't see the file in the app in settings.
Is there something wrong?
And I am curious about how to restore to realm db in iCloud.
Thank you
func uploadDatabaseToCloudDrive()
{
let fileManager = FileManager.default
//let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
let iCloudDocumentToCheckURL = iCloudDocumentsURL.appendingPathComponent("default.realm")
let realmArchiveURL = iCloudDocumentToCheckURL
if(fileManager.fileExists(atPath: iCloudDocumentsURL.path ))
{
do
{
try fileManager.removeItem(at: realmArchiveURL)
let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL)
}catch
{
print("ERR")
}
}
else
{
print("Need to store ")
let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL)
}
}
When using the "new" iOS 10 Core Data setup with loadPersistentStores, how can I reset/delete everything? I would like to avoid using the entity names but would love to use something like destroyPersistentStore.
I setup my stack the following:
persistentContainer = NSPersistentContainer(name: "CoreData", managedObjectModel: mom)
let storeDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let url = storeDirectory.appendingPathComponent("CoreData.sqlite")
let description = NSPersistentStoreDescription(url: url)
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
persistentContainer.persistentStoreDescriptions = [description]
persistentContainer.loadPersistentStores(completionHandler: { (_, error) in
guard let error = error as NSError? else { return }
fatalError("Unresolved error: \(error), \(error.userInfo)")
})
persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
persistentContainer.viewContext.undoManager = nil
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
I only found solutions with e.g. BatchDeleteRequests where I need to know all entity names, but I would like something more generic and reset everything - I need the most performant/safest way when the user logs out.
This suggestion performs the following steps
Get the store from the persistentStoreCoordinator
Save the URL of the store
Reset the managed object context
Destroy the store
Add a new store
The code assumes the standard implementation of the iOS 10+ NSPersistentContainer API with persistentStoreCoordinator and managedObjectContext properties in AppDelegate and a SQLite store.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let persistentStoreCoordinator = appDelegate.persistentContainer.persistentStoreCoordinator
let currentStore = persistentStoreCoordinator.persistentStores.last!
let currentStoreURL = currentStore.url!
appDelegate.managedObjectContext.reset()
do {
try persistentStoreCoordinator.destroyPersistentStore(at: currentStoreURL, ofType: NSSQLiteStoreType)
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: currentStoreURL)
} catch {
print(error)
}
In iOS I'd recommend to add a new store immediately because normally the application won't terminate and therefore the persistent container won't be recreated.
I have an app with local/offline Realm files/databases. I would like to add the option to log in to sync data across devices, and I figured Realm Object Server would be great for this. I know that when a user logs in, I will need to write a migration from a local realm to a synced realm. What do I do if a user decides to logout? What do I do if a user never logs in in the first place? Do I need to have two paths in my code (everywhere) to choose whether it should use the local or synced realm?
I use a RealmProvider class to get the realm instance.
class RealmProvider {
class func realm() -> Realm {
if let _ = NSClassFromString("XCTest") {
return try! Realm(configuration: Realm.Configuration(fileURL: nil, inMemoryIdentifier: "test", syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: 0, migrationBlock: nil, deleteRealmIfMigrationNeeded: true, objectTypes: nil))
} else {
let user = getUser()
guard let loggedInUser = user else {
do {
return try Realm()
} catch {
print(error)
}
return try! Realm()
}
// open up synced url to the username.
if let user = SyncUser.current {
let syncServerURL = URL(string: "realm://realm.myapp.com/~/\(loggedInUser.username.md5())")!
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: syncServerURL))
// Open the remote Realm
return try! Realm(configuration: config)
} else {
// login first.
let serverURL = URL(string: "http://realm.myapp.com")!
let token = getToken()
let customCredentials = SyncCredentials(customToken: token, provider: Provider("custom/myAuth"))
//
SyncUser.logIn(with: customCredentials,
server: serverURL) { user, _ in
if let user = user {
// can now open a synchronized Realm with this user
// Open Realm
let configuration = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://realm.myapp.com/~/\(loggedInUser.username.md5())")!)
)
_ = try! Realm(configuration: configuration)
}
}
}
return try! Realm()
}
}
}
To answer your question,
What do I do if a user decides to logout?
The RealmProvider will give you a local realm file.
What do I do if a user never logs in in the first place?
The data will still be stored in a local realm file, once the user logs in, you can open up a synced realm and run the migration.
PS: There is one catch, if the synced realm is being opened the first time, it will still return the local realm file. :/
I'm pretty new to ios development.
I follow this migration example to use pre-populated database and change the code a little bit
here is the final code I use on AppDelegate -> func application
let defaultPath = Realm.Configuration.defaultConfiguration.path!
let path = NSBundle.mainBundle().pathForResource("default", ofType: "realm")
if let bundledPath = path {
print("use pre-populated database")
do {
try NSFileManager.defaultManager().removeItemAtPath(defaultPath)
try NSFileManager.defaultManager().copyItemAtPath(bundledPath, toPath: defaultPath)
} catch {
print("remove")
print(error)
}
}
I'm testing this in a real device.
It works but according to the code logic, it'll always be reset to the pre-populated database. This is verified: the data is reset after app restart.
I tried moveItemAtPath instead of copyItemAtPath. permission error
I tried to delete the pre-populated database file after copy. permission error
I tried to use the pre-populated database file as the realm default configuration path. error occurs too.
In Swift 3.0, try this:
let bundlePath = Bundle.main.path(forResource: "default", ofType: "realm")
let destPath = Realm.Configuration.defaultConfiguration.fileURL?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destPath!) {
//File exist, do nothing
//print(fileManager.fileExists(atPath: destPath!))
} else {
do {
//Copy file from bundle to Realm default path
try fileManager.copyItem(atPath: bundlePath!, toPath: destPath!)
} catch {
print("\n",error)
}
}
Yeah, your logic is correct. Every time this code gets executed, the default Realm file in the Documents directory is deleted and replaced with the static copy that came with the app bundle. This is done by design in the Realm sample code in order to demonstrate the migration process each time the app is launched.
If you only want that to happen one time, the easiest way to do it would be to check beforehand to see if a Realm file already exists at the default path, and then perform the copy only when it isn't already there. :)
let alreadyExists = NSFileManager.defaultManager().fileExistsAtPath(defaultPath)
if alreadyExists == false && let bundledPath = path {
print("use pre-populated database")
do {
try NSFileManager.defaultManager().removeItemAtPath(defaultPath)
try NSFileManager.defaultManager().copyItemAtPath(bundledPath, toPath: defaultPath)
} catch {
print("remove")
print(error)
}
}
try this
let realm_db_path = Realm.Configuration.defaultConfiguration.fileURL!
let bundle_realm_path = Bundle.main.url(forResource: "default", withExtension: "realm")!
if !FileManager.default.fileExists(atPath: realm_db_path.absoluteString){
do {
try FileManager.default.copyItem(at: bundle_realm_path, to: realm_db_path)
}catch let error {
NSLog(error as! String)
}
I'd like to delete databases of realm.
I know how to delete that on Java, but I need to do that on swift
like this(Java):
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this).build();
Realm.deleteRealm(realmConfig);
realm = Realm.getInstance(realmConfig);
thanks.
If you want to delete Realm's files
let manager = NSFileManager.defaultManager()
let realmPath = Realm.Configuration.defaultConfiguration.path as! NSString
let realmPaths = [
realmPath as String,
realmPath.stringByAppendingPathExtension("lock")!,
realmPath.stringByAppendingPathExtension("log_a")!,
realmPath.stringByAppendingPathExtension("log_b")!,
realmPath.stringByAppendingPathExtension("note")!
]
for path in realmPaths {
do {
try manager.removeItemAtPath(path)
} catch {
// handle error
}
}
From Realm's official documentation: https://realm.io/docs/swift/latest/#deleting-realm-files
Best thing to do is to call deleteAll() method on realm object like:
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}