How to backup realm db to iCloud with Swift? - swift

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)
}
}

Related

SWIFT writing to plist is not updating

I'm trying to write to the plist and I'm using two approaches but none of them work for me.
I'm not getting any errors though and when I print the paths I can see that plist exist, however you can see from the screenshot that the plist it is not getting updated/populated.
let path = Bundle.main.path(forResource: "Employee", ofType: "plist")!
let data : NSDictionary =
["A": [["userid":"1","username":"AAA","usergroupid":"2"], ["userid":"33","username":"ABB","usergroupid":"8"]],
"B": [["userid":"2","username":"BBB","usergroupid":"8"], ["userid":"43","username":"ABC","usergroupid":"8"]] ]
//first approach
let favoritesDictionary = NSDictionary(object: data, forKey: ("Favorites" as NSString?)!)
print(path)
let succeeded = favoritesDictionary.write(toFile: path, atomically: true)
//second approach
let bundlePath = Bundle.main.path(forResource: "Employee", ofType: "plist")!
print(bundlePath)
let dictionary = NSMutableDictionary(contentsOfFile: bundlePath)
dictionary?.setObject(data, forKey: ("Locations" as NSString?)!)
dictionary?.write(toFile: bundlePath, atomically: true)
Can someone please help?
This is a short tutorial.
Create your plist file and put it in the application bundle.
In AppDelegate create a computed property to get the current Documents folder and append the file path
var employeePlistURL : URL {
let documentsFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsFolderURL.appendingPathComponent("Employee.plist")
}
In AppDelegate applicationWillFinishLaunching register a key-value pair for the firstLaunch flag in UserDefaults and copy the plist into the documents folder if the flag is true
func applicationWillFinishLaunching(_ aNotification: Notification) {
let defaults = UserDefaults.standard
defaults.register(defaults: ["firstLaunch":true])
if defaults.bool(forKey: "firstLaunch") {
let sourceFile = Bundle.main.url(forResource: "Employee", withExtension: "plist")!
try? FileManager.default.copyItem(at: sourceFile, to: employeePlistURL)
defaults.set(false, forKey: "firstLaunch")
}
}
Wherever you need to read and write the property list create also the computed property and add a property for the dictionary
var employees = [String:Any]()
and two methods to load and save the data
func loadEmployees() {
do {
let data = try Data(contentsOf: employeePlistURL)
guard let plist = try PropertyListSerialization.propertyList(from: data, format: nil) as? [String:Any] else { return }
employees = plist
} catch { print(error) }
}
func saveEmployees() {
do {
let data = try PropertyListSerialization.data(fromPropertyList: employees, format: .binary, options: 0)
try data.write(to: employeePlistURL)
} catch { print(error) }
}
A better way is to use structs and PropertyListEncoder/-Decoder but as the literal dictionary and the screenshot in the question are rather different I provide the common Dictionary / PropertyListSerialization way.

How to handle optional Realm sync?

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. :/

why when i open existing realm database is always zero result

why when i open existing realm database is always zero result
func chekDB() {
let bundleDB = Bundle.main.path(forResource: "Conversio", ofType: "realm")
let desPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let fileManager = FileManager.default
let fullDesPath = URL(fileURLWithPath: desPath).appendingPathComponent("Conversio.realm")
let fullDestPathString = String(describing: fullDesPath)
if fileManager.fileExists(atPath: fullDesPath.path){
print("Database file is exis !")
print(fileManager.fileExists(atPath: bundleDB!))
}else{
do{
try fileManager.copyItem(atPath: bundleDB!, toPath: fullDesPath.path)
}catch{
print("error encured while copying file to directori \(fullDestPathString)")
}
}
}
and when i call it with
let realm = try! Realm()
let data = realm.object(some.self)
print(data.count) // => 0 ??
You should specify realm file path to open your realm file:
let realm = try! Realm(fileURL: YOUR_REALM_FILE_URL)
Lear more at https://realm.io/docs/swift/latest/#realms

Realm Object Server. Sync initial Local DB

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. :)

How do i remove databases with Realm?

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()
}