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 {}
}
Related
I am implementing a small logger, in which I am writing to a TXT file.
I wanted the last event to be at the top of the file but I'm having trouble getting this to work. All examples on the internet are using "fileHandle.seekToEndOfFile()" to write at the end of the file.
This is what I have:
private static func writeToFile(text: String) {
guard let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { return }
guard let writePath = NSURL(fileURLWithPath: path).appendingPathComponent(Logger.folderName) else { return }
let fileManager = FileManager.default
try? fileManager.createDirectory(atPath: writePath.path, withIntermediateDirectories: true)
let file = writePath.appendingPathComponent(Logger.fileName)
if !fileManager.fileExists(atPath: file.path) {
do {
try "".write(toFile: file.path, atomically: true, encoding: String.Encoding.utf8)
} catch _ {
}
}
let msgWithLine = text + "\n"
do {
let fileHandle = try FileHandle(forWritingTo: file)
//fileHandle.seekToEndOfFile()
fileHandle.write(msgWithLine.data(using: .utf8)!)
fileHandle.closeFile()
} catch {
print("Error writing to file \(error)")
}
}
With this code, I write in the first line but nevertheless, I am always rewriting what is on the first line.
How can I change this to whatever is in the first be taken to a line below and then write new content in the first line?
Thank you!!
This should be okay:
First, get the old data and after that append the new one and write everything.
let msgWithLine = text + "\n"
do {
let fileHandle = try FileHandle(forWritingTo: file)
fileHandle.seek(toFileOffset: 0)
let oldData = try String(contentsOf: file, encoding: .utf8).data(using: .utf8)!
var data = msgWithLine.data(using: .utf8)!
data.append(oldData)
fileHandle.write(data)
fileHandle.closeFile()
} catch {
print("Error writing to file \(error)")
}
I didn't test the code it may have some problems.
Another possible solution is to write at the end of the file and to invert the file when you read it.
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 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)}
}
I am trying to append a string into text file. I am using the following code.
let dirs : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if (dirs) != nil {
let dir = dirs![0] //documents directory
let path = dir.stringByAppendingPathComponent("votes")
let text = "some text"
//writing
text.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
//reading
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
println(text2) //prints some text
}
this does not append the string to file. Even if I call this function repeatedly.
If you want to be able to control whether to append or not, consider using OutputStream. For example:
do {
let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("votes.txt")
guard let outputStream = OutputStream(url: fileURL, append: true) else {
print("Unable to open file")
return
}
outputStream.open()
let text = "some text\n"
try outputStream.write(text)
outputStream.close()
} catch {
print(error)
}
By the way, this is an extension that lets you easily write a String (or Data) to an OutputStream:
extension OutputStream {
enum OutputStreamError: Error {
case stringConversionFailure
case bufferFailure
case writeFailure
}
/// Write `String` to `OutputStream`
///
/// - parameter string: The `String` to write.
/// - parameter encoding: The `String.Encoding` to use when writing the string. This will default to `.utf8`.
/// - parameter allowLossyConversion: Whether to permit lossy conversion when writing the string. Defaults to `false`.
func write(_ string: String, encoding: String.Encoding = .utf8, allowLossyConversion: Bool = false) throws {
guard let data = string.data(using: encoding, allowLossyConversion: allowLossyConversion) else {
throw OutputStreamError.stringConversionFailure
}
try write(data)
}
/// Write `Data` to `OutputStream`
///
/// - parameter data: The `Data` to write.
func write(_ data: Data) throws {
try data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) throws in
guard var pointer = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
throw OutputStreamError.bufferFailure
}
var bytesRemaining = buffer.count
while bytesRemaining > 0 {
let bytesWritten = write(pointer, maxLength: bytesRemaining)
if bytesWritten < 0 {
throw OutputStreamError.writeFailure
}
bytesRemaining -= bytesWritten
pointer += bytesWritten
}
}
}
}
For Swift 2 rendition, see previous revision of this answer.
You can also use FileHandle to append String to your text file. If you just want to append your string the end of your text file just call seekToEndOfFile method, write your string data and just close it when you are done:
FileHandle usage Swift 3 or Later
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// create a new text file at your documents directory or use an existing text file resource url
let fileURL = documentsDirectory.appendingPathComponent("simpleText.txt")
do {
try Data("Hello World\n".utf8).write(to: fileURL)
} catch {
print(error)
}
// open your text file and set the file pointer at the end of it
do {
let fileHandle = try FileHandle(forWritingTo: fileURL)
fileHandle.seekToEndOfFile()
// convert your string to data or load it from another resource
let str = "Line 1\nLine 2\n"
let textData = Data(str.utf8)
// append your text to your text file
fileHandle.write(textData)
// close it when done
fileHandle.closeFile()
// testing/reading the file edited
if let text = try? String(contentsOf: fileURL, encoding: .utf8) {
print(text) // "Hello World\nLine 1\nLine 2\n\n"
}
} catch {
print(error)
}
Please check the below code as its working for me. Just Add the code as it is:
let theDocumetFolderSavingFiles = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let filePath = "/theUserData.txt"
let thePathToFile = theDocumetFolderSavingFiles.stringByAppendingString(filePath)
let theFileManager = NSFileManager.defaultManager()
if(theFileManager.fileExistsAtPath(thePathToFile)){
do {
let stringToStore = "Hello working fine"
try stringToStore.writeToFile(thePathToFile, atomically: true, encoding: NSUTF8StringEncoding)
}catch let error as NSError {
print("we are geting exception\(error.domain)")
}
do{
let fetchResult = try NSString(contentsOfFile: thePathToFile, encoding: NSUTF8StringEncoding)
print("The Result is:-- \(fetchResult)")
}catch let errorFound as NSError{
print("\(errorFound)")
}
}else
{
// Code to Delete file if existing
do{
try theFileManager.removeItemAtPath(thePathToFile)
}catch let erorFound as NSError{
print(erorFound)
}
}
A simple solution that works for me. UPDATE, it looks like I must have gotten this from here, so credit where credit is due:
Append text or data to text file in Swift
Usage:
"Hello, world".appendToURL(fileURL: url)
Code:
extension String {
func appendToURL(fileURL: URL) throws {
let data = self.data(using: String.Encoding.utf8)!
try data.append(fileURL: fileURL)
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
Check the reading part.
The method cotentsOfFile: is a method of NSString class. And you have use it wrong way.
So replace this line
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
Here you have to use NSString instead of String class.
let text2 = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
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".