iCloud Drive cannot remove file created from other device - ios

I'm trying to adopt iCloud Drive to store backups of the data of my application using a single file for each backup, if it's relevant they're simple XML files with a custom extension. File creation and upload are working fine but as I'm now trying to let the user manually delete backups I found out that I cannot delete the file programmatically but I can do it if I go to iCloud Drive from the app on iOS or the folder in Finder on macOS.
To save the files I first retrieve the root for the container with
FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
and create the Document folder if it doesn't exists as I want the user to be able to view and edit the files, after creating the file locally in the temporary directory I upload it with
let file = FileManager.default
if file.fileExists(atPath: remotePath.path) {
try? file.removeItem(at: remotePath)
}
try? file.setUbiquitous(true, itemAt: localPath, destinationURL: remotePath)
where remotePath is the path retrieved before with the file name appended. To load the files I use a NSMetadataQuery and get the path of each file with NSMetadataItemURLKey on each returned item, which is working fine but when I attempt to
try? FileManager.default.removeItem(at: retrievedPath)
it works if the same device created the file but if it is from another one it always fail and the error says that the file does not exists at the specified path.
I've removed error handling code for clarity but I've tested this behaviour inspecting the thrown error.
How can I delete any file even from other devices, am I missing some steps?

I was indeed missing something, after reading this question, in particular this answer, I found out that even if the NSMetadataQuery returns the path correctly is possible that the file has not been downloaded to the the device, hence the error.
To correctly delete a file you have to mimic the behaviour of UIDocument and use a NSFileCoordinator on a background queue, refer to the answer referenced before for implementation details.

Related

What happens to local files on iOS when the app is updated?

My iOS app is writing to local files to:
/var/mobile/Containers/Data/Application/A.../Library/a.txt
When my app updates, it gets a new application container ID and folder:
/var/mobile/Containers/Data/Application/B...
What happens to all the files I wrote to container ID A?
Is there an "update hook" that will allow me to copy all the "A" container files to path "B"?
Can the new version of the app (B) read the old versions files (A)?
Is there any documentation around what happens to the filesystem during updates?
Is it possible to recover files from container A after B has been installed?
When you update an app, by changing its version number in the .plist file, iOS creates a new directory for that app with a different hexadecimal name and it copies all the files to the new directories. Now if you are using the absolute paths to get the details of files from the directories then those paths would be incorrect and you won't get the file details.
I just tried this in simulator. I created a function as below which will return the document directory path url
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
I called this function from didFinishLaunchingWithOptions in app delegate when version number was 1.0 and it returned the below path :
file:///Users/BhargavRathod/Library/Developer/CoreSimulator/Devices/3082611F-BCCA-4D17-B390-E0EF4CA454DA/data/Containers/Data/Application/72759097-38F3-4292-825E-1D2343219973/Documents/
When I updated the version number to 1.1 it returned me the new path as :
file:///Users/BhargavRathod/Library/Developer/CoreSimulator/Devices/3082611F-BCCA-4D17-B390-E0EF4CA454DA/data/Containers/Data/Application/72DC31E9-C32F-42CC-8449-F1946ADB1018/Documents/
So if you are using a absolute path to access any file from document directory then it is not good practice. You can just save the file name(or relative path after the document directory) and whenever the access to that file name is required then get the file name and append it after the document directory path and access the file.
I hope this will be of some help to you.
My iOS app is writing to local files to: /var/mobile/Containers/Data/Application/A.../Library/a.txt
Okay, stop right there. This is wrong.
Your app has its own sandbox. This sandbox persists forever, thru updates, as long as your app keeps its Bundle ID, and as long as the user does not delete the app.
You thus have no business knowing or thinking about the full absolute path to where your file is. All you know, and all you need to know, is that it is in your app’s sandbox in the Library directory. You can, at any time, obtain the URL of the Library directory by means of its search path:
https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/librarydirectory
And that is where the file will always be. As long as you ask the FileManager for your Library directory and for the a.txt file within it, you will find the same file, regardless of any updates, as long as the user doesn’t actually delete your app (because that deletes the sandbox).
Can explain what happens to the files
Nothing. They stay where they are within the sandbox. The absolute URL of the sandbox may change, but your files are unaffected.
how to keep the files written by the previous version of the app
They are kept automatically. You don’t have to do anything.
(Having said all that, keep in mind that if you submit an app with a different bundle id, that is not a new version of your app. It is a totally different app. In that case you would have a very different problem to solve. This would be no different from any other problem of communicating files from one app to another. You’d need to put the files in a common location, make them available thru the Document Browser, make them exportable by the user, or whatever.)

Unable to Locate Realm File on Device

I am attempting to locate the realm DB file on my device (iPhone) and I followed the steps here and here. However, I do not see any realm files in the folder AppData/Documents.
I print out the URL path as I run the app on my device and my simulator, and this is what I see:
//On device
/private/var/mobile/Containers/Shared/AppGroup/C7332EE4-1567-4C96-8392-7FBD0DC9C863/DB/default.realm
//On simulator
/Users/MYNAME/Library/Developer/CoreSimulator/Devices/D205C50D-F432-4A1F-80CB-FB3D91E0A1EA/data/Containers/Shared/AppGroup/5E67A740-A338-4583-9B9D-461F5D1A734A/DB/default.realm
The URL written on the simulator appears to be any default URL where the Realm DB is written to. I can't seem to understand why can't I find the realm file at AppData/Documents on the actual device.
Realm files are only created on disk when you call Realm() for the first time. Once you've created an instance of Realm, you can find the exact file route of that Realm by printing out realm.configuration.fileURL. You can normally use Realm.Configuration to set a different location of the Realm file on disk as opposed to the Documents directory.
Both of those file paths you displayed show that the file is located in a folder named DB and it appears to be in an app group (eg, a shared folder between apps and extensions) instead of your app's default Documents directory.
So with that being said, it would appear that somewhere in your app, a custom Realm.Configuration object is being used to create the Realm file somewhere else, which would explain why you can't find it in Documents.

How to get files which are locally stored in app's document directory after ios app version get update?

I have developed an iOS app that has been downloading files and storing data in the Documents directory. I am storing document’s directory path (where downloaded files get store) in sqlite database. Now if App version get changes then path for database file and document’s directory get changes. If I have manually delete the app and install it again then downloaded files get removed from document’s directory and user has to download files again. Is there any way to get my database file with previously downloaded document’s directory path so that no need to download files again Or any other solution for this?
Understand this concept for document directory
If you update the newer version of the App, Data in the document directory will not getting erased. It will be there. Just the path to reach the document directory get's updated.
So, to overcome the path changing problem. Don't store the entire path to your database file. Just store the ending path or name of the downloaded content to the document directory. Now, if you wants to get some file, lets say example.jpg from the document directory, then you should first get the path to document directory and then append the path of your file.
PATH_TO_DOCUMENT_DIRECTORY/example.jpg this is the complete path for your image. and same is applies for your database.sqlite file.
You can use this handy functions:
func documentsDirPath() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as [String]
return paths[0]
}
And can call it in this way:
let pathToDocumentDirectory = documentsDirPath()
let fileName = "example.jpg" // This you can get from the database.
let filePath = pathToDocumentDirectory + "/\(fileName)"
print(pathToDocumentDirectory)
And for the app that get's deleted, there is no option to recover the contents of the document directory, unless you code and save the data to iCloud and on newer installation of the app, in applicationDidFinishLaunching check if there is some files, present on iCloud with name database.sqlite and other, then you can fetch it and put it to document directory and later you can use.
Hope it helps

How do I get my pre-populated Default.realm file onto a device?

I have a realm file that is already populated with data that needs to be there when the app is loaded on a device.
What can I do to get the realm file onto my device for testing and what do I need to do to make sure it is already there when someone downloads the app from the app store?
I am using Swift.
Add your database file to the Xcode project, i.e. "preloaded.realm"
Make sure you select the add to targets, when first dropping in your file
Then (taking from the migration example) you can do something like this to copy that preloaded file to your default directory. This will create a read/write realm
// copy over old data files for migration
let defaultPath = RLMRealm.defaultRealmPath()
let defaultParentPath = defaultPath.stringByDeletingLastPathComponent
let v0Path = NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("preloaded.realm")
NSFileManager.defaultManager().removeItemAtPath(defaultPath, error: nil)
NSFileManager.defaultManager().copyItemAtPath(v0Path, toPath: defaultPath, error: nil)
Here is a link to that general code https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift-2.2/Migration/AppDelegate.swift
You'll first have to create the realm file you want to ship with your app. Once you have that, add it to your app's Xcode project and copy it into the bundle (which Xcode should do automatically).
At this point, the app should be able to access the bundled file (you can use NSBundle.mainBundle().pathForResource(_:ofType:) to get the path).
You can either create a read-only realm at this path (see RLMRealm(path:readOnly:error:)), or copy it to your Documents directory to create a read-write realm file.
You should refer to our migration example for more details on how to do this.

hidden files with .icloud extension in ubiquity container

I am building app which syncs documents over the iCloud.
When I create document (lets say its name is myfile1.doc) in one device and expect to appear in another one I get nothing - it looks like it doesn't sync. To my surprise when I read the content of Documents folder in ubiquity container I can see the representation of the file is added as a hidden file with .icloud extension (in this case .myfile1.doc.icloud).
I tried to find more about this .icloud files but so far no results.
Anyone know what are they representing? Is it the metadata item that indicates that file was added to iCloud and I should manually download it?
I think hidden files are handled by metadataQuery to indicate that the new file was added to iCloud and is waiting to be sync'ed. NSMetadataQuery recognizes them as representation of new files not yet downloaded to ubiquity container.
These files are not documented and you should ignore them. If you want to know what's in them, open one in a hex editor and have a look. Your code should not check for them or access them in any way.

Resources