Reset schema for all Realms in Realm Cloud - ios

Is there any way I can completely nuke everything from my Realm Cloud, including existing schema definitions?

There is a way to delete the Realms from the Realm Object Server.
Here's information I collected on a Realm Forums Post
Here's a link to the official docs.
This is super important though. The docs I am linking are for Docs 3.0. Self-hosting appears to be going away so the 3.16 Docs no longer include this information.
There are two steps
Remove server files
Remove all local files
These both have to be done or else Realm will try to re-sync itself and your data will never go away.
The first function deletes a Realm Cloud instance and if successful, deletes the local realm files.
//
//MARK: - delete database
//
func handleDeleteEverything() {
let realm = RealmService //Singleton that returns my realm cloud
try! realm.write {
realm.deleteAll()
}
guard let currentUser = SyncUser.current else {return}
let adminToken = currentUser.refreshToken!
let urlString = "https://your_realm.cloud.realm.io" //from RealmStudio upper right corner
let endPoint = "\(urlString)/realms/files/realm_to_delete"
let url = URL(string: endPoint)
var request = URLRequest(url: url!)
request.httpMethod = "DELETE"
request.addValue(adminToken, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let err = error {
print("err = \(err.localizedDescription)")
return
}
print("Realm has been deleted")
self.deleteLocalRealmFiles() //remove local files
}
task.resume()
}
and then the function to remove the local files. This function is a bit different than what appears on the Realm Forums post with the addition of this function in Realm 4.2
try Realm.deleteFiles(for: config)
and the function that calls it
func deleteLocalRealmFiles() {
do {
let config = Realm.Configuration.defaultConfiguration
let isSuccess = try Realm.deleteFiles(for: config)
if isSuccess == true {
print("local files were located and deleted")
} else {
print("no local files were deleted, files were not found")
}
} catch let error as NSError {
print(error.localizedDescription)
}
}

I think, you can check this link.
https://forum.realm.io/t/is-it-possible-to-reset-the-default-realm-without-creating-a-new-instance/1466
This one solve my realm database issue and can reset all schemas
I hope it works well!

Related

Realm file for unit testing path permission (Xcode, Swift)

Please forgive me if there is answered question similar to this. I tried to find any but couldn't find it
Hi guys, i am having problem with running Realm file for my unit testing
So i have moved the realm files to my project test folder
SampleAppTest
+ SampleAppTest.swift
+ SampleRealm.realm
I can successfully located the realm file, however, when I tried to instantiate the Realm connection, it threw me an error saying
Please use a path where your app has read-write permissions.
let bundle = Bundle(for: type(of: self))
if let path = bundle.path(forResource: "SampleRealm", ofType: "realm") {
let realmLocationURL = URL(string: path)
let realmVersion: UInt64 = 1
let config = Realm.Configuration(fileURL: realmLocationURL, schemaVersion: realmVersion, migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < realmVersion) {}
}
)
let realm = try! Realm(configuration: config) // throws error
}
else {
print("path not found")
}
Folder access in Finder -> Get Info -> Read & Write
So in general, where can I put a file that my app has a permission to read-write? Is there a possible way to put within the project so that other team member that clone the project can also run the test with same realm file? Thank you
Storing Realm in your App Bundle makes it a read only realm so I don't think that was the intention.
The default location Realm uses for your Realm files is always a good bet - however, it won't travel with your project files.
During development, we often store our realm file with the project - that way if the project is compressed, sent or shared (or put it on dropbox) the file is always available and is read/write. Here's some code to do that:
func gGetRealm() -> Realm? {
do {
let fileUrl = URL(fileURLWithPath: #file)
let projectSubUrl = fileUrl.deletingLastPathComponent() //the project files folder
let projectUrl = projectSubUrl.deletingLastPathComponent() //the project folder
let realmURL = projectUrl.appendingPathComponent("default.realm")
var config = Realm.Configuration.defaultConfiguration
config.fileURL = realmURL
let realm = try Realm.init(configuration: config)
return realm
} catch let error as NSError { //just throw the error to the console
print("Error!")
print(" " + error.localizedDescription)
let err = error.code
print(err)
let t = type(of: err)
print(t)
return nil
}
}
This is global function to get the Realm stored within the project file - use is
guard let realm = gGetRealm() else { return }
let results = realm.objects(MyObject.self)

Realm file management

We have been using Realm for a while and some of our users have been experiencing some data loss related to Realm. We think we have narrowed it down to our compaction method for when the file gets too big. We would like to ask for a little advice on if this is the proper way to recreate our Realm file. This method is called on applicationDidEnterBackground.
We wrote a sample of what we are doing below:
public static func compact() {
// Get the original file path
let configuration = RealmSampleClient.shared.config
guard let originalFileURL = configuration.fileURL else {
return
}
// check if the file size is bigger than 10mb, if not return
guard let attr = try? FileManager.default.attributesOfItem(atPath: originalFileURL.absoluteString),
let fileSize = attr[FileAttributeKey.size] as? UInt64,
fileSize > 500_000_000 else {
return
}
// create a filepath for a copy
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMddHHmmss"
let dateString = "\(dateFormatter.string(from: date)).realm"
let copyFileURL = originalFileURL.deletingLastPathComponent().appendingPathComponent(dateString)
// copy the Realm file
do {
let realm = try Realm(configuration: configuration)
try realm.writeCopy(toFile: copyFileURL, encryptionKey: configuration.encryptionKey)
} catch {
return
}
// remove the old file and copy the new one
do {
removeRealmFile(at: originalFileURL)
try FileManager.default.copyItem(at: copyFileURL, to: originalFileURL)
} catch {
}
// remove a copy if it exists
guard FileManager.default.fileExists(atPath: copyFileURL.path) else { return }
do {
try FileManager.default.removeItem(at: copyFileURL)
} catch {
}
}
private static func removeRealmFile(at url: URL = databaseUrl) {
let realmURLs = [
url,
url.appendingPathExtension("lock"),
url.appendingPathExtension("note"),
url.appendingPathExtension("management"),
]
realmURLs.forEach { URL in
guard FileManager.default.fileExists(atPath: URL.path) else { return }
do {
try FileManager.default.removeItem(at: URL)
} catch {
}
}
}
Thanks your your help
I can see no kind of compacting code here, only a copy of the database file. So I assume you have left that out to keep the code compact here.
Anyway, you do this operation when the app enters background mode. Do you register a background task for that? If the compacting operation takes too much time the task gets killed by iOS, I think this is the problem.
You can explicitly ask the OS for more background execution time with UIApplication.shared.beginBackgroundTask but this is also a very time limited amount, usually 3 minutes.
But this is all digging in the dark, you should post more code to see how your background task is set up.
As per Realm doc, it's recommended to wrap your code with autoreleasepool, like this
do {
let realm = try Realm(configuration: configuration)
try realm.writeCopy(toFile: copyFileURL, encryptionKey: configuration.encryptionKey)
} catch {
return
}
and doing this in a backgroundtask will definitely help, a friendly advice is to always handle errors, you are just returning in the catch block, a log may help ..
Looking more into the doc I can see that RealmSwift integrates a compacting feature now, more details here :
https://realm.io/docs/swift/latest/#compacting-realms
We refactored the flow of our app a bit and it has seemed to solve our problems. It was related to accessing realm too soon during the startup process, possible on multiple threads.

URLSession results in NIL data

I'm trying to learn Swift, and I have a little project with Google's places API.
I have a method for fetching places details, which uses URLSession in swift to send the request:
func fetchRestaurantDetails(placeId: String) -> Void {
let jsonURLString = "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeId)&key=[MY API KEY]"
guard let url = URL(string: jsonURLString) else { return}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
_ = session.dataTask(with: urlRequest) { (data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.
self.rest = place.result
} catch {
print("error trying to convert data to JSON")
return
}
}.resume()
}
I use this method to create a instance of type Restaurants, which I will later add to a list:
func createRestaurant(placeId: String) -> Restaurants {
self.fetchRestaurantDetails(placeId: placeId)
let rest = Restaurants(name: self.rest.name,
formatted_address: self.rest.formatted_address,
website: self.rest.website,
location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
opening_hours: self.rest.opening_hours.weekday_text,
photo: restImg)
return rest!
}
But whenever I reach back into the "let rest = Restaurants(...)" all the values are nil. When I try to debug it, it just jumps over my "_ = session" sections right down to resume(), then back to session again and ends back at resume(). No data produced.
I'm quite puzzled since I successfully executed this piece of code before, and now I'm wondering if I missed something.
Thx :-)
Put two breakpoints. One at
let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.
self.rest = place.result
and the second one at
let rest = Restaurants(name: self.rest.name,
formatted_address: self.rest.formatted_address,
website: self.rest.website,
location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
opening_hours: self.rest.opening_hours.weekday_text,
photo: restImg)
You will realise that the second one is getting called first.
You are fetching data, which is done asynchronously, and before its available you are trying to use it. You need to make sure that the data is available before you use it. One way here would be to use completion handler. You can learn about completion handlers here.
fetchRestaurantDetails is an asynchronous method due to the fact that you call session.dataTask in it, which is asynchronous.
You are trying to use the results of the function before it actually returned. You have several ways to solve this issue:
Use a completion handler to return the value from fetchRestaurantDetails
Use DispatchGroups to detect when the URLRequest finished
Use a 3rd party framework like PromiseKit to handle the asynchronous functions like normal functions with return values.

Image URL to UIImage Not Working

let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
self.postImage.image = UIImage(data: data)
}
}
task.resume()
I have the following code that takes a url from firebase, in the form http://i.imgur.com/nkomPpP.jpg, and is supposed to turn that url into a UIImage that can be placed on a view. However, while extracting the text from the firebase object works, parsing the image URL doesn't seem to be working as I get an empty view. What am I doing wrong?
I know why, your code works. The problem is your image link. Your imageURL's HTTP type. iOS don't like HTTP type request because it's not safe.
Plan A: Try a HTTPS type image link, it works.
Plan B: Add "App Transport Security Settings" in project info ,and set "Allow
Arbitrary Loads" yes in "App Transport Security Settings" dictionary.
I suggested use Plan A, that's Apple want iOSDev to do.
You need to remove the () from after DispatchQueue.main.async(). Try this:
let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
self.postImage.image = UIImage(data: data)
}
}
task.resume()

How do I retrieve cached files in iOS swift?

Thanks for the help in my last question. This time I would like to ask for help again for an application whose contents need to be downloaded and cached when it's opened for the first time.
Indeed it's a web app where the view controller consists of a WebView. In order to cache the whole website (which consists of "index.htm", "first.htm, "second.htm" and etc), I have scraped the whole site using the Kanna library and hence generated numerous links (generatedURL). Then I write the HTML of each link into a single file using the approach answered here. Read and write data from text file
Here is my code in the didFinishLaunchingWithOptions of AppDelegate.swift.
// get the documents folder url
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
for index in 0..<generatedURL.count {
let fileDestinationUrl = documentDirectoryURL.URLByAppendingPathComponent(String(index)+".htm")
cachedURL[index] = fileDestinationUrl //store the cached urls
let fileURL = NSURL(string: generatedURL[index])
//if (NSFileManager.defaultManager().fileExistsAtPath(fileDestinationUrl)) {
let data = NSData(contentsOfURL: fileURL!)
if (data != nil) {
//writing to disk
data?.writeToURL(fileDestinationUrl, atomically: true)
// saving was successful. any code posterior code goes here
//reading from disk
do {
let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
print(fileDestinationUrl)
print(mytext) // "some text\n"
} catch let error as NSError {
print("error loading from url \(fileDestinationUrl)")
print(error.localizedDescription)
}
}
// } else {
// print("The files already exist")
// //reading from disk
// do {
// let mytext = try String(contentsOfURL: fileDestinationUrl, encoding: NSUTF8StringEncoding)
// //print(fileDestinationUrl)
// //print(mytext) // "some text\n"
// } catch let error as NSError {
// print("error loading from url \(fileDestinationUrl)")
// print(error.localizedDescription)
// }
//
// }
}
When running the program, the HTMLs of all the links are stored locally in those files. There's no problems in loading the HTML and thereby displaying the cached page in the WebView.
file:///var/mobile/Containers/Data/Application/.../Documents/0.htm
file:///var/mobile/Containers/Data/Application/.../Documents/1.htm
file:///var/mobile/Containers/Data/Application/.../Documents/2.htm
.
.
.
However, the current problem is that I lost the linkage between the cached pages. For example, in the website, there is a button on "index.htm" that links to "first.htm".
Now after loading the cached "index.htm" which is now "file:///var/....../0.htm", I won't be able to go to the cached "first.htm" because "file:///var/....../1.htm" is not in the HTML of the button.
So how do I retrieve the cached files in their original urls? Should I change the approach of generating the file or just create a new version of the website with all the cached file paths?
Thanks for reading my question.
OK, i think I can answer my own question now. Using the following function in the ViewController.swift containing the webView object, I can prompt the webView to load the cached url if the original url is clicked.
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == UIWebViewNavigationType.LinkClicked {
if (request.URL!.absoluteString == generatedURL[index] {
let requestObj = NSMutableURLRequest(URL: appDelegate.cachedURL[index]!);
webView.loadRequest(requestObj)
//return false
}
}
return true
}

Resources