I am creating a binary file using few c libraries and saving the file in documents folder of app in iOS. But when i try to read it its not reading. I am using the following code to read.
let fileData = try NSData(contentsOfFile: filePath, options: NSData.ReadingOptions.mappedIfSafe) as Data
But this goes always into catch block.
You must retrieve your Document folder first and then get the content of document.
func readDocument(file:String) -> NSData{
var vreturn:NSData
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true){
let dir = dirs[0] //documents directory
let path = dir.stringByAppendingString(file);
//reading
vreturn = (try? NSData(contentsOfFile: path)) ?? NSData()
}
return vreturn
}
I'm using this to get String content but it seems to work the same way using NSData. Simple way to read local file using Swift?
do {
let videoData = try Data(contentsOf: avsset.url)
print(videoData.count)
} catch let err {
print("Error:", err)
}
you must catch error...
Related
I'm trying to display a .log file in a textview but can't seem to access the filepath properly. When I do it returns nil. I'm using a pod called "SwiftyBeaver" to do the logging. This is what the fileURL looks like:
file:///var/mobile/Containers/Data/Application/5C92E3E6-9E45-4869-9142-AB9E70EE4FCC/Library/Caches/swiftybeaver.log
This is the function I'm using to turn the .log into a string so I can display it in a textView
private func loadTextWithFileName(_ fileName: String) -> String? {
guard let path = Bundle.main.path(forResource: fileName, ofType: "log"),
let contents = try? String(contentsOfFile: path) else {return nil}
return contents
}
This is how I'm displaying the text to the textView
self.loggingTextView.text =
self.loadTextWithFileName(self.file.logFileURL!.absoluteString)
The method that you are using Bundle.main.path() is mainly to search for files in your Bundle.
But it seems your log file is going to be in your Cache directory.
Here is how you can look for a file in your Cache directory of your app
private func loadTextWithFileName(_ fileName: String) -> String? {
if let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(fileName)
guard let text = try? String(contentsOf: fileURL, encoding: .utf8) else {
return nil
}
return text
}
return nil
}
You can add a catch block to your try case to check what the error is, in case you don't get the log file details.
I am a little lost on my quest to -
Download a CKAsset (PDF File)
Assign a Temporary Filename
Write the contents of the CKAsset to the filename
I have managed to download the CKAsset and display the contents in a UIWebView, however I am stumbling over steps 2 and 3, I have a filename from a String, and despite trying a variety of WriteToFile combinations I receive errors.
My code is thus :
let filename = record.object(forKey: "materialsFilename")
if materialsType == "PDF" || materialsType == "pdf" {
if let asset1 = record.object(forKey: "materialsFile") as? CKAsset {
let doc1Data : NSData? = NSData(contentsOf:asset1.fileURL)
let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename as! String)
let contentsOfFile = doc1Data
var error: NSError?
// Write File
if contentsOfFile.writeToFile(path, atomically: true, encoding: String.Encoding.utf8, error: &error) == false {
if let errorMessage = error {
print("Failed to create file")
print("\(errorMessage)")
}
} else {
print("File \(filename) created at tmp directory")
}
This version presents the error -
Cannot invoke 'writeToFile' with an argument list of type '(URL?,
atomically: Bool, encoding: String.Encoding, error: inout NSError?)'
The temporary file once created will be passed to a UIActivityViewController, to print / email / airdrop the PDF, having only a CKAsset name, the the UIActivityViewController cannot associate the file type to any of the users installed apps, save for print.
After a little head scratching and reviewing my choices following the pointers above, I changed tack and didn't really need to write to a file, just rename the CKAsset, which I achieved with the following script -
let materialsType = record.object(forKey: "materialsType") as! String
let filename = record.object(forKey: "materialsFilename") as! String
if materialsType == "PDF" || materialsType == "pdf" {
if let asset1 = record.object(forKey: "materialsFile") as? CKAsset {
let doc1Data : Data? = Data(contentsOf:asset1.fileURL) as Data?
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename)
self.materialsWebView.load(doc1Data! as Data, mimeType: "application/pdf", textEncodingName: "UTF-8", baseURL: NSURL() as URL)
self.filenameURL = [(fileURL)]
}
The key seemed to hinge on two lines -
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename)
and
self.filenameURL = [(fileURL)]
Which generates the filename for the UIActivityViewController and thus opens up the access to a number of additional Apps.
Are you sure you're using the correct writeToFile method? The NSData reference doesn't show a method with the same signature you're using. Try using one of the ones listed in the reference.
I only find the following function in Data class.
func write(to: URL, options: Data.WritingOptions)
Try this.
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
}
Below code is work. The images are saved successfully in document directory, but the problem is only first time the collectionViewController can load images with path successfully. I have to delete all images to store new images or it will show the error message
"fatal error: unexpectedly found nil while unwrapping an Optional
value".
Because the path is unavailable, readnsdata = NSData(contentsOfFile: filepath)! will cause error.
I have no idea why only the first time it can work.
path :
"/var/mobile/Containers/Data/Application/29306029-BDCF-4BEA-93A6-D5626CBAAA90/Documents/x.jpg"
func writeNSDataToDisk(imageData:NSData){
let myindex = imgPathArray.count
let fileName = "\(self.imgPathArray.count)"
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let docs: String = paths[0] as String
let filepath: String = (docs as NSString).stringByAppendingPathComponent("\(fileName).jpg")
let test = imageData.writeToFile(filepath, atomically: true)
if test {
self.imgPathArray.insert(filepath, atIndex: myindex)
print("The picture \(fileName).jpg is been saved.")
self.readORwriteList(true)//write list to txt file
}
print(self.imgPathArray)
}
func readNSDataFromDisk(fileIndex:Int) -> NSData{
let checkValidation = NSFileManager.defaultManager()
var readnsdata = NSData()
if (fileIndex <= self.imgPathArray.count) {
let filepath = self.imgPathArray[fileIndex]
if (checkValidation.fileExistsAtPath(filepath)){
print("File is available")
print("load \(fileIndex).jpg,filepath is \(filepath)")
readnsdata = NSData(contentsOfFile: filepath)!
if readnsdata.length != 0 {
getImageProperties(readnsdata)
}
}
else{
print("File is not available!!!")
}
}
return readnsdata
}
The solution to my problem :
Instead of storing absolute file path, I name the files in a regular way and search them by their name. There is no need to store path.
The URLs for the files are now constructed relative to the Documents directory URL every time the app is run.
Thanks
First a side note. Apple's docs specifically recommend against using fileExistsAtPath the way you're doing it here.
NOTE
Attempting to predicate behavior based on the current state of
the file system or a particular file on the file system is not
recommended. Doing so can cause odd behavior or race conditions. It’s
far better to attempt an operation (such as loading a file or creating
a directory), check for errors, and handle those errors gracefully
than it is to try to figure out ahead of time whether the operation
will succeed.
Try replacing this…
if (checkValidation.fileExistsAtPath(filepath)){
print("File is available")
print("load \(fileIndex).jpg,filepath is \(filepath)")
readnsdata = NSData(contentsOfFile: filepath)!
if readnsdata.length != 0 {
getImageProperties(readnsdata)
}
}
else{
print("File is not available!!!")
}
…with this…
do {
readnsdata = try NSData(contentsOfFile: filepath, options: .DataReadingMappedIfSafe)
if readnsdata.length != 0 {
getImageProperties(readnsdata)
}
}
catch let e {
print("Couldn't read file at \(filepath) because \(e)")
}
This approach gives you the information you were looking for without having to speculate. Just run your code and see what happens when the NSData initializer throws! :)
[Update: Off-topic opinion]
While it's a good habit not to sprinkle a long method with returns, there's not a lot going on here. Personally, I think the code comes out more readable without the temporary readnsdata variable. This way, imo, both the happy path and the default return values are clear on first reading:
func readNSDataFromDisk2(fileIndex:Int) -> NSData{
if (fileIndex <= self.imgPathArray.count) {
let path = self.imgPathArray[fileIndex]
do {
let data = try NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
if data.length != 0 {
getImageProperties(data)
}
return data
}
catch let e {
print("Couldn't read file at \(path) because \(e)")
}
}
return NSData()
}
replace readnsdata = NSData(contentsOfFile: filepath)! with readnsdata = NSData(contentsOfFile: filepath)?. hope this will help :)
I am trying to set up an app with an In App Purchase that downloads content for 12 levels of a game when that respective pack is purchased.
I am stuck on how to properly move the downloaded images from the cache folder to the Documents folder. Here is my code so far:
func processDownload(sender: NSURL) {
//Convert URL to String, suitable for NSFileManager
var path:String = sender.path!
path = path.stringByAppendingPathComponent("Contents")
//Makes an NSArray with all of the downloaded files
let fileManager = NSFileManager.defaultManager()
var files: NSArray!
do {
files = try fileManager.contentsOfDirectoryAtPath(path)
} catch let err as NSError {
print("Error finding zip URL", err.localizedDescription)
}
//For each file, move it to Library
for file in files {
let pathSource: String = path.stringByAppendingPathComponent(file as! String)
let pathDestination: String = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0]
//Remove destination files b/c not allowed to overwrite
do {
try fileManager.removeItemAtPath(pathDestination)
}catch let err as NSError {
print("Could not remove file", err.localizedDescription)
}
//Move file
do {
try fileManager.moveItemAtPath(pathSource, toPath: pathDestination)
print("File", file, "Moved")
}catch let err as NSError {
print("Couldn't move file", err.localizedDescription)
}
}
}
Everything actually works just fine except for the errors that are printing from the two do statements. When trying to remove any existing files of the same name in the first do block, I get the following error:
Could not remove file “Library” couldn’t be removed because you don’t have permission to access it.
This subsequently causes the next error from the next do statement to print because the original could not be removed.
Any ideas of why this is happening and how I can properly save the downloaded files elsewhere? Thanks.
I've found a proper working solution. This code will move all of the items in the downloaded zip folder to the Library directory.
func processDownload(sender: NSURL) {
//Convert URL to String, suitable for NSFileManager
var path: String = sender.path!
path = path.stringByAppendingPathComponent("Contents")
//Makes an NSArray with all of the downloaded files
let fileManager = NSFileManager.defaultManager()
var files: NSArray!
do {
files = try fileManager.contentsOfDirectoryAtPath(path)
} catch let err as NSError {
print("Error finding zip URL", err.localizedDescription)
}
//For each file, move it to Library
for file in files {
let currentPath: String = path.stringByAppendingPathComponent(file as! String)
var pathDestination: String = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0]
pathDestination = pathDestination.stringByAppendingPathComponent(file as! String)
//Move file
do {
try fileManager.moveItemAtPath(currentPath, toPath: pathDestination)
print("File", file, "Moved")
}catch let err as NSError {
print("Couldn't move file", err.localizedDescription)
}
}
}
I can now make SKTextures in SpriteKit with these files like so:
var rippleTex = SKTexture(image: UIImage(contentsOfFile: NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0].stringByAppendingPathComponent("P06_ripple.png"))!)