I'm trying to read and write a file from a path (ex: "/Desktop/folder"). If this can't be done, then from Documents (ex: "/Documents/folder"). I saw and tried several examples, but the problem is that the file is located in a location such:
file:///Users/name/Library/Developer/CoreSimulator/Devices/AE6A47DE-D6D0-49AE-B39F-25C7A2335DC8/data/Containers/Data/Application/09F890C1-081F-46E7-88BC-F8453BAFC1CB/Documents/Test.txt"
0x00006000000af780
Even if i have the "Test.txt" in Documents and even in project.
Here's the code which reads and writes a file at the above location:
let file = "Test.txt" //this is the file. we will write to and read from it
let text = "some text" //just a text
var text2 = ""
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(file)
//writing
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */print(error)}
//reading
do {
text2 = try String(contentsOf: fileURL, encoding: .utf8)
}
catch {/* error handling here */ print(error)}
}
Is it possible to read and write file from path i need (ex: "Documents/Folder")?
So, like you're doing now, take the documents dir, and append the path you need:
let file = "Test.txt" //this is the file. we will write to and read from it
guard let dir = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first { else return }
let subDir = dir.appendingPathComponent("Folder", isDirectory: true)
let fileURL = subDir.appendingPathComponent(file)
Note that trying to write to that file URL will fail if the sub-folder "Folder" doesn't already exist. You'd have to use one of the file manager createDirectory calls to create the "Folder" directory if it doesn't exist.
I find the solution:
let file = "Test.txt" //this is the file. we will write to and read from it
let text = "some text" //just a text
var text2 = ""
let fileURL = URL(fileURLWithPath: "/Users/name/Documents/Folder/Test.txt")
//writing
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */print(error)}
//reading
do {
text2 = try String(contentsOf: fileURL, encoding: .utf8)
var s = ""
}
catch {/* error handling here */ print(error)}
}
Related
I am wondering what the work-around is for downloading files with irregular filenames using Swift's FileManager. For example, downloading a file named "Hello/Goodbye" where the file path looks like:
let filePath = documentDirectory.appendingPathComponent("\(fileName).m4a")
will result in the file downloading to a folder inside documentDirectory named 'Hello' since filePath is "documentDirectory/Hello/Goodbye.m4a". Instead, I want the file to be downloaded under documentDirectory as 'Hello/Goodbye.m4a'. Is there anyway to encode these special characters so that the file path ignores them?
If you need to add a slash "/" to your filename you need to replace it by a colon ":":
let desktopDirectory = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
let fileName = "Hello/Goodbye.txt".replacingOccurrences(of: "/", with: ":")
let file = desktopDirectory.appendingPathComponent(fileName)
do {
try "SUCCESS".write(to: file, atomically: true, encoding: .utf8)
} catch {
print(error)
}
extension URL {
func appendingFileName(_ fileName: String, withExtension: String) -> URL {
appendingPathComponent(fileName.replacingOccurrences(of: "/", with: ":")).appendingPathExtension(withExtension)
}
}
let fileName = "Hello/Goodbye"
let pathExtension = "txt"
let file = desktopDirectory.appendingFileName(fileName, withExtension: pathExtension)
do {
try "SUCCESS".write(to: file, atomically: true, encoding: .utf8)
} catch {
print(error)
}
I am creating an OCR app where I am saving results of scans in files with this code.
let localFileName = String("file.rtf")
let text = String(“Scan result text”)
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(localFileName)
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {}
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
print("Read from file \(text2)")
}
catch {}
}
This code is working and creating files somewhere and I am able to verify that this code is working. Now I want that files and their contents accessible to user of my app. How can I do that? Is there a way to create files in public directory?
I'm trying to save a CSV file to the Documents folder on iOS. The write(to:atomically:encoding:) doesn't throw an error, but when I got to load the file I get the error:
The file “test.csv” couldn’t be opened because there is no such file.
How can I resolve this? Also, if I try to put the file in a folder in the Documents directory, it consistently fails writing the file saying there is "no folder named "test.csv"". I tried using .txt instead of .csv and got the same error.
My write block
let dobString = NEKFormatter.dateOfBirth.string(from: dob)
// TODO: File Naming Scheme
let fileName = "test.csv"
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
let destDir = documentsDirectory
let destPath = destDir.appendingPathComponent(fileName)
// Attempt to save file to patient folder. If no patient folder exists, create it.
do {
if FileManager.default.fileExists(atPath: destDir.absoluteString) {
try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: false, attributes: nil)
}
print(destPath)
try csvString.write(to: destPath, atomically: true, encoding: .utf8)
filePath = destPath.absoluteString // Global variable
} catch {
debugPrint("Error writing file: \(error.localizedDescription),")
}
My load block
let rawDataPath = URL(fileURLWithPath: filePath)
do {
let csvString = try String(contentsOf: rawDataPath, encoding: .utf8)
} catch {
debugPrint(error.localizedDescription, #file, #line)
}
var file = ""
var text = ""
var path: URL?
override viewDidLoad(){
super.viewDidLoad()
file = "test2.csv" //this is the file I will write to
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
path = dir.appendingPathComponent(file)
do {
text = "Hello"
try text.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
getInsightResult()
}
This piece of code is writing "Hello" to "test2.csv" perfectly in my viewDidLoad() method. However, when I run that excerpt of code in a separate method called getInsightResult(), which I call in viewDidLoad, the text that is being written is blank. However, when I print out the text it is not empty, but displays the correct text.
func getInsightResult() {
for x in 0...4{
for y in 0...4{
if(y != 3){
do{
let temp = arrayOfDataArrays[y]
text = String(temp[x])
print("Tester:\(temp[x])")
print("Text:" + text)
try text.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
}
text = "\n"
do{try text.write(to: path!, atomically: true, encoding: String.Encoding.utf8)}
catch {/* error handling here */}
}
}
The issue is merely a misconception on your part: you seem to imagine that String's write method appends to an existing file, and it doesn't. It replaces the contents of the file.
Your code is thus working perfectly. The text is being written to the file, every time you say text.write.... The "problem" is that this line:
text = "\n"
do{try text.write(to: path!, atomically: true, encoding: String.Encoding.utf8)}
...replaces everything in the file with a single linefeed. And since that is the last line executed, that is the state of the file when we finish.
try this
let file = "file.txt"
let text = "text content"
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let path = dir.appendingPathComponent(file)
do {
try text.write(to: path, atomically: false, encoding: String.Encoding.utf8)
}
catch {}
}
Having searched through the many (many!) swift playground questions to even craft this code, I'm still struggling.
I've placed a text file in the Resources folder of package contents, and it appears as an alias (link) in the running temp files generated by the playground (/var/folders/ ...).
import UIKit
let bundle = NSBundle.mainBundle()
let myFilePath = bundle.pathForResource("dict1", ofType: "txt")
println(myFilePath) // <-- this is correct, there is a shortcut to the Resource file at this location
var error:NSError?
var content = String(contentsOfFile:myFilePath!, encoding:NSUTF8StringEncoding, error: &error)
println(content!) // <-- this is *NOT* the file contents [EDIT: see later note]
// Demonstrate there's no error
if let theError = error {
print("\(theError.localizedDescription)")
} else {
print("No error")
}
The problem being, that content is shown in the playground output as being Some "apple\ngame\nhow\nswift\ntoken", rather than the file contents as expected.
It's finding the file, because if I change the filename, it errors. Any advice on getting the file contents?
Xcode 6.1
EDIT:
So, the actual problem was that I wasn't expecting the playground output (including, println) to be escaped. That, combined with fatigue and other stupidities led me to believe there was a problem, when none existed.
Interestingly, not everything seems to be escaped in playground:
println("foo\nbar") // Outputs "foo\nbar", escaped
println("\\n") // Outputs "\n", unescaped
You can try creating a class for opening and saving your files:
edit/update: Swift 5 or later
class File {
class func open(
_ path: String,
encoding: String.Encoding = .utf8
) throws -> String {
guard FileManager.default.fileExists(atPath: path) else {
throw NSError(
domain: "NSCocoaErrorDomain",
code: 260,
userInfo: [
"NSUnderlyingError": #"Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory""#,
"NSFilePath": path
]
) as Error
}
return try String(
contentsOfFile: path,
encoding: encoding
)
}
class func save(
_ path: String,
_ content: String,
encoding: String.Encoding = .utf8
) throws {
try content.write(
toFile: path,
atomically: true,
encoding: encoding
)
}
}
Usage: File.save
let stringToSave: String = "Your text"
if let fileURL = FileManager.default.urls(
for: .desktopDirectory,
in: .userDomainMask
).first?.appendingPathComponent("file.txt") {
do {
try File.save(fileURL.path, stringToSave)
print("file saved")
} catch {
print("Error saving file:", error)
}
}
Usage: File.open
if let fileURL = FileManager.default.urls(
for: .desktopDirectory,
in: .userDomainMask
).first?.appendingPathComponent("file.txt") {
do {
let loadedText = try File.open(fileURL.path)
print("Loaded text:", loadedText)
} catch {
print("Error reading file:", error)
}
}
Or if you prefer extending StringProtocol:
extension StringProtocol {
func open(from directory: FileManager.SearchPathDirectory = .documentDirectory,
in domain: FileManager.SearchPathDomainMask = .userDomainMask,
encoding: String.Encoding = .utf8) throws -> String {
let directory = try FileManager.default.url(
for: directory,
in: domain,
appropriateFor: nil,
create: true
)
return try String(
contentsOf: directory.appendingPathComponent(.init(self)),
encoding: encoding
)
}
func save(as fileName: String,
to directory: FileManager.SearchPathDirectory = .documentDirectory,
in domain: FileManager.SearchPathDomainMask = .userDomainMask,
encoding: String.Encoding = .utf8) throws {
let directory = try FileManager.default.url(
for: directory,
in: domain,
appropriateFor: nil,
create: true
)
try write(to: directory.appendingPathComponent(fileName),
atomically: true,
encoding: encoding)
}
}
Usage iOS (saving/loading from documents directory):
let stringToSave: String = "Your text"
let fileName = "file.txt"
do {
try stringToSave.save(as: fileName)
print("Text saved!!!")
let loadedText = try fileName.open()
print("Text loaded:", loadedText)
} catch {
print("Error:", error)
}
Usage macOS (saving/loading from desktop directory):
let string = "Your text"
let fileName = "file.txt"
do {
try string.save(as: fileName, to: .desktopDirectory)
print("Text saved!!!")
let loadedText = try fileName.open(from: .desktopDirectory)
print("Text loaded:", loadedText)
} catch {
print("Error:", error)
}
I have seen this problem with .txt files created from .rtf files using TextEdit.
I loaded a text.txt file in the resources folder of my playground using similar code to you. The file contents was "hello there" and was made by converting an .rtf file to .txt by changing the extension.
let path = NSBundle.mainBundle().pathForResource("text", ofType: "txt")//or rtf for an rtf file
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)
The output was:
{\rtf1\ansi\ansicpg1252\cocoartf1343\cocoasubrtf140
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
\f0\fs24 \cf0 hello there}
So the "hello there" is embedded. This is the problem with all the layout information in a .rtf file.
I went back to TextEdit and created a true .txt file. After opening a file - Format|Make Plain Text
Now this same code gave the console output "hello there".