Saving Cache Files to Disk in iOS - ios

I am working on an Application which deals with audio files. For this application, it is best not to stream the files, since you might download the files to your device and then disconnect from the server and listen to them later, perhaps in areas with no cell reception.
I created a caching system that stores the data to a file path and then stores the file location in CoreData with it's associated data.
self.name = name
var domainMask = UserDefaults.standard.string(forKey: "userDomainMask")
if domainMask == nil {
domainMask = NSSearchPathForDirectoriesInDomains(.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first!
UserDefaults.standard.setValue(domainMask, forKey: "userDomainMask")
}
var cachePath = domainMask!
cachePath = (cachePath as NSString).appendingPathComponent(DataCache.cacheDirectoryPrefix + name)
self.cachePath = cachePath
ioQueue = DispatchQueue(label: DataCache.ioQueuePrefix + name)
self.fileManager = FileManager()
}
But when I try it on device, it is able to access the data at the file path, but it fails to load the file and play it.
The kicker is, when I share a file from my phone, it is able to find the file and upload it to the server, then retrieve it from the cache and display and play the file. If I re-run the application, it returns to a state where it can access the data, but the audio file does not load.

Related

is the temporaryDirectory of FileManager automatically removed by iOS? if so when?

I am adding an option in my App to backup everything to a file.
For that, I am serializing all my objects to a dictionary that I save in a single file in the path provided by FileManager.default.temporaryDirectory. Then I ask the user for a path to save this file using a UIDocumentPickerViewController.
Everything works fine, but I wonder what happens with the temporal file that I created in the temporaryDirectory. Is this automatically removed by the operating system? should I take care of removing it by myself?
This is how I get the path of the temporary directory for my file:
private var exportURL: URL {
let documentURL = FileManager.default.temporaryDirectory
let fileName = "archive"
let filePath = "\(fileName).backup"
return documentURL.appendingPathComponent(filePath)
}

Monitor file with some kind of persistent id

My app writes files to the users shared Documents directory. I save the files path but this breaks if the user changes the documents name, moves the document to a new location etc. Is there some kind of persistent file id i can use to keep track of these documents?
i've been poking around
FileManager.default.attributesOfItem(atPath: docUrl.path)
and i find attributes like FileAttributeKey.systemFileNumber but this number seems to change every time i run the app. There has to be some way to keep track of files other than their path
You need to use url bookmarks, like the following (sketch)
if let bookmark = try? URL(fileURLWithPath: docUrl.path)
.bookmarkData(includingResourceValuesForKeys: nil,
relativeTo:nil) {
// store somewhere persistent bookmark to your file
}
...
let bookmark = // read bookmark from persisten storage
var stale = false
if let url = try? URL(resolvingBookmarkData:bookmark, bookmarkDataIsStale: &stale) {
// work with resolved url
} else {
// file disappeared - handle the situation
}

Files in tmp directory still taking up space after deletion ios

I am saving media data to the iPhones tmp directory and after it is no longer needed I delete all the files. However I have noticed in my phones settings menu, under storage that my applications memory is increasing every time I save a new file and then later delete it. I am pretty sure the files are being removed because if I try to delete the same file again it throws an exception that it doesn't exist. Here is my code for deleting everything in tmp:
let content = try FileManager.default.contentsOfDirectory(atPath: NSTemporaryDirectory())
for path in content {
let fullPath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(path)
try FileManager.default.removeItem(atPath: fullPath.absoluteString)
}
The weird thing is that sometimes after not using the application and coming back the memory goes down, keyword being sometimes. Am I doing something wrong and/or is there anything else I can do to minimize the memory footprint of my application in regards to the tmp folder?
It turns out passing it a NSURL vs string was the solution, now the files are definitely being deleted:
for path in content2 {
let filePath = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(path)
try FileManager.default.removeItem(at: filePath!)
}

How to find Apple App Group shared directory

We are currently developing an iOS10 app, including "Messages Extension".
To share CoreDatas persistant store.sqlite inbetween App and Extension, we are using a shared "Apple App Group" directory, which is working fine.
Now we have to get our hands on the store for debug reasons and are unable to find the directory. The Apps container directories are completely empty, which makes sense. But how to download our database? Do we have to somehow copy it programmatically to a reachable place?
To sum it up:
We already use CoreData which stores model.sqlite in our shared directory.
Everything is up and running.
What we want to archive is to download the database to our computer.
Without a shared directory we can simply download the App container from the device, using Xcode->Devices. But as we do use a shared directory, the .sqlite database is not within the container.
Question:
How can we download the .sqlite database from the device to our computer?
EDIT on 2018-10-12: Updated code for Swift 4.x (Xcode 10). (Older version retained for reference.)
In Swift 4.x:
let sharedContainerURL :URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.etc.etc")
// replace "group.etc.etc" above with your App Group's identifier
NSLog("sharedContainerURL = \(String(describing: sharedContainerURL))")
if let sourceURL :URL = sharedContainerURL?.appendingPathComponent("store.sqlite") {
if let destinationURL :URL = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("copyOfStore.sqlite") {
try! FileManager().copyItem(at: sourceURL, to: destinationURL)
}
}
In older version of Swift (probably Swift 2.x):
let sharedContainerURL :NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.etc.etc") // replace "group.etc.etc" with your App Group's identifier
NSLog("sharedContainerURL = \(sharedContainerURL)")
if let sourceURL :NSURL = sharedContainerURL?.URLByAppendingPathComponent("store.sqlite")
{
if let destinationURL :NSURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0].URLByAppendingPathComponent("copyOfStore.sqlite")
{
try! NSFileManager().copyItemAtURL(sourceURL, toURL: destinationURL)
}
}
Something like the above will get a file from the app group's shared container to the app's Documents directory. From there, you could use Xcode > Window > Devices to get it to your computer.
You could also use iTunes file sharing to retrieve the file from the app's Documents directory after setting UIFileSharingEnabled to YES in the Info.plist file, but bear in mind that this will expose the directory's contents to the user as well. Should be okay for development/debugging purposes, though.

.realm file automatically backed up by iCloud

my app was rejected because of the size of the content that it uploads to iCloud. The only file in my app's Documents folder is the default.realm database file. I think that this is the file that iCloud is uploading. How can I prevent iCloud to upload the database to iCloud?
Thanks.
According to the App Backup Best Practices section of the iOS App Programming Guide, <Application_Data>/Library/Caches or <Application_Data>/tmp will not backup to iCloud. Generally, you can use <Application_Data>/Library/Caches directory to save your data that you won't backup to iCloud.
To change the file path of Realm, you can pass the path parameter when creating Realm instance, like below:
let realmPath = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)[0] as! String
let realm = Realm(path: realmPath.stringByAppendingPathComponent("data.realm"))
Otherwise, you can use NSURLIsExcludedFromBackupKey file system property to exclude files and directories from backups (See Technical Q&A QA1719). If you want to use the default path, there is the only way to exclude Realm file from backups.
let realm = Realm()
if let realmPathURL = NSURL(fileURLWithPath: realm.path) {
realmPathURL.setResourceValue(NSNumber(bool: true), forKey: NSURLIsExcludedFromBackupKey, error: nil)
}
Looks like the URL API has changed since the previous answer was posted. This is how you can disable the backup now:
let realm = try! Realm()
guard var url = realm.configuration.fileURL else {
return
}
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try? url.setResourceValues(resourceValues)

Resources