Copy Sqlite Database Behind CoreData - ios

I want to access the Sqlite database backing CoreData, make a copy of it, and send it to an endpoint for remote troubleshooting. Is this possible?
My solution...
let fileManager = FileManager.default
let libraryURL = NSPersistentContainer.defaultDirectoryURL()
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
// handle error...
return
}
// creating a folder in documents for ease of zipping and later removal
let dbURL = documentsURL.appendingPathComponent("db")
do {
try fileManager.createDirectory(at: dbURL, withIntermediateDirectories: true, attributes: nil)
} catch {
// handle error...
return
}
do {
let fileURLs = try fileManager.contentsOfDirectory(at: libraryURL, includingPropertiesForKeys: nil, options: [])
for fileURL in fileURLs.filter({ $0.absoluteString.contains("<NAME_OF_CORE_DATA_STORE>") }) {
let fileName = fileURL.lastPathComponent
do {
try fileManager.copyItem(at: fileURL, to: URL(string: "\(documentsURL.absoluteString)db/\(fileName)")!)
} catch {
// handle error...
}
}
} catch {
// handle error...
}
// finally, do something with the file (I zip and send to an endpoint)

Yes, possible. Just don’t try and treat it as writable outside the core data ecosystem. Readable, no worries.
Copy all the parts from where it’s written to. The default SQLite mode is journaled so you will get a wal file written as a counterpart.
You can interrogate the NSPersistentStore.url for the write location of your database

Related

iOS file path is changing at every launch/rerun the application

I have an issues in changing the file path at every launch of the app.
I have a file("AppConstant.json") in application bundle, and this file I need to copy into application document directory. I am successfully saving "AppConstant.json" file inside the created user folder "MyFolder" on Document directory.
But the problem is when I relaunch the application second time, it's not showing the same path. Also I am using relativepath, but still it not getting.
here is the code
// calling the directory
let stringAppConstant = copyFileFromBundleToDocumentDirectory(resourceFile: "AppConstant", resourceExtension: "json")
// saving or get exit file path
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) -> String
{
var stringURLPath = "Error_URLPath"
let fileManager = FileManager.default
let docURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
if !fileManager.fileExists(atPath: newDestPath.path) {
do {
try fileManager.createDirectory(atPath: newDestPath.path,withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", newDestPath.path)
} catch {
print("Error in creating folder :::",error.localizedDescription);
}
}
else {
print("Folder is already exist!")
}
if fileManager.fileExists(atPath: fullDestPath.path) {
print("File is exist in ::: \(fullDestPath.path)")
stringURLPath = fullDestPath.path
}
else {
do {
try fileManager.copyItem(atPath: sourcePath, toPath: fullDestPath.path)
print("Saved file successfully in :::", fullDestPath.path)
stringURLPath = fullDestPath.path
} catch {
print("Error in creating file ::: \(error.localizedDescription)")
}
}
return stringURLPath
}
Please help me, where I need to save the path in Sandbox. Is this right way what I implemented.
I am running in device and simulator, both path are different while relaunch
this is the path for first time launch:
/var/mobile/Containers/Data/Application/81B568A7-0932-4C3E-91EB-9DD62416DFE8/Documents/MyFolder/AppConstant.json
relaunch the application I am getting new path:
/var/mobile/Containers/Data/Application/3DAABAC3-0DF5-415B-82A5-72B204311904/Documents/MyFolder/AppConstant.json
NOTE: I create a sample project and I use this same code and it's working. But in existing project it's not working. I am using the same bundle id and profile only for both sample and project. Checked the file added reference, settings, version all are same.
Any idea?
The behavior that the container path changes periodically is normal.
These lines
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
contain a lot of mistakes
URL(string is the wrong API for file paths, it's URL(fileURLWithPath).
The second parameter of path(forResource:ofType:) must not have a leading dot.
The API absoluteString is wrong as parameter of URL(fileURLWithPath
Not a real mistake but don't use NSURL in Swift.
It's highly recommended to use always the URL related API to concatenate paths and get the documents folder from FileManager. Further it's good practice to make the method throw the real error rather than returning a meaningless literal string. And NSSearchPathForDirectoriesInDomains is outdated and should not be used in Swift.
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) throws -> URL
{
let sourceURL = Bundle.main.url(forResource: resourceFile, withExtension: resourceExtension)!
let fileManager = FileManager.default
let destFolderURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("MyFolder")
let fullDestURL = destFolderURL.appendingPathComponent(resourceFile).appendingPathExtension(resourceExtension)
if !fileManager.fileExists(atPath: destFolderURL.path) {
try fileManager.createDirectory(at: destFolderURL, withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", destFolderURL.path)
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
} else {
print("Folder already exists!")
if fileManager.fileExists(atPath: fullDestURL.path) {
print("File exists in ::: \(fullDestURL.path)")
} else {
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
}
}
return fullDestURL
}
Edit 1:
Hi I created the new project and use the same code I posted in main, and it's working. But in the real project it not working.
Not sure what exactly going on in your project, try to debug it. It's part of development as well. :)
If you are in hurry to fix this issue in this weekend try to use the following code snippet.
// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)
// try to write data in document directory
do {
let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
// use your `constFileURL`
} catch (let error as FileOperationError) {
switch error {
case .fileAlreadyExists(let url):
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8))
case .IOError(let error):
print("IO Error \(error)")
}
} catch {
print("Unknown Error \(error)")
}
// Helpers
enum FileOperationError: Error {
case fileAlreadyExists(url: URL)
case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
// final destination path
let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
// check for file's existance and throw error if found
guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
throw FileOperationError.fileAlreadyExists(url: destURLPath)
}
// Create Intermidiate Folders
let intermidiateDicPath = destURLPath.deletingLastPathComponent()
if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
do {
try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
} catch {
throw FileOperationError.IOError(error)
}
}
// File Writing
do {
try data.write(to: destURLPath, options: .atomic)
} catch {
throw FileOperationError.IOError(error)
}
return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}
Original Answer
Why don't you just return "MyFolder/\(fileName)" on successful file operation? If you need to access the path later you can always do that using FileManager APIs.
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")
// Access const file data
do {
let fileData = try Data(contentsOf: constFilePath)
// Use you data for any further checking
} catch {
// Error in reading file data
print("Error in file data access : \(error)")
}

How to get the root cause when createFile fails?

Trying to create a file using createFile API on iOS.
createFile(path, data, attr)
Document says,
Return Value:
true if the operation was successful or if the item already exists, otherwise false.
I wonder if the operation was not successful, how can I get the exact cause of the failure?. Expected some API like,
createFile(path, data, attr, &error)
Use Data.write(to:options:) instead, it throws.
I use FileManager.default.createDirectory to create folders. I create Data that I want to write, and then I use Data.write to save it to disk.
var docDir = URL()
do {
docDir = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
} catch {
fatalError("\(error)")
}
let subDir = docDir.appendPathComponents("mySubDir")
do {
try FileManager.default.createDirectory(
at: subdir,
withIntermediateDirectories: true,
attributes: nil
)
} catch {
if (error as NSError).code == 516 {
// "The file “x” couldn’t be saved in the folder “y” because a file with the same name already exists."
// This is OK.
} else {
fatalError("\(error)")
}
}
let filePath = subdir.appendingPathComponent("yourFileName")
let object:Codable = Whatever()
let data = try! JSONEncoder().encode(object)
do {
try data.write(to: filePath)
} catch {
fatalError("\(error)")
}
I do not know why Apple devs chose FileManager.createFile to be different. It seems strange to me.

Swift delete all files from particular Document Directory Location

My Scenario, I am trying to delete all files from particular document directory by using document folder path. Here, every time I am saving file within my application document directory folder, by using below code I can’t able to delete files
let urlString: String = myurl.absoluteString
print("FILEURL:\(urlString)")
do {
try fm.removeItem(atPath: "\(myurl)")
} catch let error as NSError {
print(error.debugDescription)
}
You are mixing up URL and String path
Either use the String related API
try fm.removeItem(atPath: myurl.path) // NEVER use .absoluteString for a file system path
or use the URL related API (recommended)
try fm.removeItem(at: myurl)
To remove all files get the file URLs in the enclosing directory with contentsOfDirectory(at:includingPropertiesForKeys:options:) and remove one by one
let fileManager = FileManager.default
do {
let documentDirectoryURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURLs = try fileManager.contentsOfDirectory(at: documentDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for url in fileURLs {
try fileManager.removeItem(at: url)
}
} catch {
print(error)
}
For delete specific file or folder using Url
let filePathString = "file:///Users/mac-01/Library/Developer/CoreSimulator/Devices/092D2386-5B43-4D98-8DCF-F21E08CCD400/data/Containers/Data/Application/C6D910A2-67D9-48A4-8221-5C81C722D508/Documents/Products"
guard let fileUrl = URL(string: "\(filePathString)") else { return }
do {
try FileManager.default.removeItem(at: fileUrl)
print("Remove successfully")
}
catch let error as NSError {
print("An error took place: \(error)")
}
removeItem method is accept url of document directory file or folder.Please try with it.
For Delete Document directory folder use
let fileManager = FileManager.default
let myDocuments = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
try fileManager.removeItem(at: myDocuments)
} catch {
return
}

Why are deleted files coming back after a new write to file? Swift 2.0

I am writing an app in swift that logs sensor data to a txt file. When I have an event occur that needs to be logged I create the filename
func createNewLogFile (){
// Create a new file name
currentFileName = "log\(NSDate()).txt"
//get the path
let paths = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
//create the file
_ = paths[0].URLByAppendingPathComponent(currentFileName)
}
After the file is created I write data to the new file like this:
func writeData (data: String){
// get the path to document directory
let paths = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let filePath = paths[0].URLByAppendingPathComponent(currentFileName)
//get the data to be logged
let stringLocation = data
let stringData = stringLocation.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
//look to see if the file exist
if NSFileManager.defaultManager().fileExistsAtPath(filePath.path!) {
do {
//seek to the end of the file to append data
let fileHandle = try NSFileHandle(forWritingToURL: filePath)
fileHandle.seekToEndOfFile()
fileHandle.writeData(stringData)
fileHandle.closeFile()
} catch {
print("Can't open fileHandle \(error)")
}
} else {
do {
// write to new file
try stringData.writeToURL(filePath, options: .DataWritingAtomic)
} catch {
print("Can't write to new file \(error)")
}
}
}
When I delete the files (from a different ViewController or the same, I tried both)
I am calling this DeleteAllFiles
func deleteAllFiles (Extension: String){
let dirs = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let dir = dirs[0]
do {
let fileList = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(dir, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions())
//return fileList as [String]
for elements in fileList{
do{
try NSFileManager.defaultManager().removeItemAtURL(elements)
print("old Files has been removed")
} catch let error as NSError {
print(error.localizedDescription)
}
}
}catch let error as NSError {
print(error.localizedDescription)
}
}
I then refresh the list and the files seem to be gone.(even when I go back and forth between views) However, when I write a new file and refresh the list the files are back with the new file.
This even happens when I delete them from iTunes using the shared files feature.
Any ideas on why this is happening? I am not getting any helpful error messages.
I found the fix for the problem.
When I was creating the file I actually only meant to create the file name. There was no reason to actually create the file at this time. I am creating the actual file when I write to it.
func createNewLogFile (){
// Create a new file name
currentFileName = "log\(NSDate()).txt"
//Removed creating actual file code
}

Having trouble saving a file in a folder inside Document directory in Swift 2

I am trying to save a file inside a subfolder in Documents directory but it won't save. I can't seem to find what's wrong. Here is what I've tried:
if let audioUrl = NSURL(string: "http://pillar.foundationu.com/wp-content/plugins/pillar-data-sync/php/htmlBreakdownResult.json") {
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
do {
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(documentsUrl, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions())
print(directoryContents[0].path!)
if NSFileManager().fileExistsAtPath(String(audioUrl.path!)) {
print("The file already exists at path")
} else {
// if the file doesn't exist
// just download the data from your url
if let myAudioDataFromUrl = NSData(contentsOfURL: audioUrl){
// after downloading your data you need to save it to your destination url
if myAudioDataFromUrl.writeToURL(NSURL(fileURLWithPath: directoryContents[0].path!), atomically: true) {
print("file saved")
} else {
print("error saving file")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
Here is where I want to save the file:
/Users/rendell/Library/Developer/CoreSimulator/Devices/5A052CC5-FD34-44FD-B060-24D6F1970860/data/Containers/Data/Application/37753B0B-FAB0-478D-A7F8-98E3039D07DD/Documents/MyFolder2
But it keeps on giving me "error saving file".
First of all, use always writeToURL:options:error to get a (more) descriptive error message.
The issue is quite simple: You forgot to provide a file name.
Technically you're going to overwrite an existing folder with data. That's not possible.

Resources