Write to file not working (Swift 4) - swift

I am starting learning about Swift programming. Up to now, I have already developed my first working app. Although simple, it is a very useful one. But with the introduction of Swift 4 and XCode 9, I'm facing some headaches. Right now I am trying to write a small piece of code to write a string to a file, like this:
let fileName = "myFile.txt"
let path = NSURL(fileURLWithPath:
NSTemporaryDirectory()).appendingPathComponent(fileName)
var myText = "Some text to write to file"
do {
try myText.write(to: path!, atomically: true, encoding: UTF8)
} catch {
// Handle error
}
When I write myText.write, XCode suggests the above syntax, but right after I choose it and fill the placeholders, XCode displays the error "Extra argument 'atomically' in call. Note that it is the very structure it suggested. I haven't found any workaround until now. Can anyone help me?

The error is misleading. The encoding parameter is wrong
try myText.write(to: path!, atomically: true, encoding: .utf8)
And don't use NSURL in Swift 3+, use native URL:
let url = URL(fileURLWithPath: ...
However I recommend this way
let fileName = "myFile.txt"
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
let myText = "Some text to write to file"
let data = Data(myText.utf8)
do {
try data.write(to: url, options: .atomic)
} catch {
print(error)
}

Related

Can you perform file I/O in a REPL on Repl.it when using Swift?

There's an online site here called Repl.it that gives you an in-browser REPL environment for a ton of languages. It's great to prove out code that you post here on SO. (I think you can even include it here actually but I wasn't successful in embedding mine.)
Anyway, when using Swift, I'm wondering if it's possible to perform file read/write persistence up there. I haven't found any articles that say yes, but I have found some that show them talking about how much storage you have, and it is supposed to be the full Swift runtime with all features, so I'm not sure.
This code fails however, saying it can't be performed.
import Foundation
let file = "file.txt" //this is the file. we will write to and read from it
let text = "some text" //just a text
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 {
print(error)
}
//reading
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
print("Read back in '\(text2)'")
}
catch {/* error handling here */}
}
else{
print("Couldn't get document directory")
}
You can open it here... Swift File Persistence REPL
I admit I'm 90% sure this isn't the right place for this, but since Repl.it does let you play with and execute Swift and this is a question about what Swift is needed to accomplish this, I figured I'd try!

How can I write to a file, line by line in Swift 4

I need help figuring out how to write repeatedly to the same, open, output file.
I am using Swift 4.2. My searches and tests have turned up only code that writes a single text string to a file and then closes the file. The next opening overwrites the last one. An example is shown below.
The problem is that I need to be able to write large numbers of records (say, 1.5 million) and perform calculations on each record just before it is written to a file. That’s not feasible when the code will only write once before closing. I'm calling this "writing line by line", much like the opposite, to "read line by line."
I tried to find an option in various Swift write statements and SO posts, but everything seems to be geared toward writing once then closing the file. I tried an open for append, but that did not work and anyway it seems inefficient to open, close, reopen-append each time I want to write to a file. I tried some C code in Swift, using open(… and freopen(… but could not get something that the compiler wouldn't complain about. Hopefully, there is a way to do this all in Swift. The following code works nicely for one write.
let file0 = “test_file.txt”
let s0 = ("This is a test line of text")
do {
try s0.write(to: NSURL(fileURLWithPath: file0) as URL, atomically: false, encoding: String.Encoding.utf8)
} catch {
print("Problem writing to file0")
}
How can I adapt this code snippet to write a string, and then another and another etc, and before closing the file when it’s all done? If not with this, is there Swift code that will do the job?
Following are the essential code components needed to write to a file, line-by-line in Swift. First is some file management code to create a file if it does not exist, then there is code to print a series of example statements, followed by code to print to the file in a loop, and finally close the file. This code worked correctly in Swift 4.2. The difference between this and the method in the question is that the write statements in this code use a method of fileHandle! and the question shows a method of a Swift string.
print("Swift_Write_to_File_Test_1")
var outFilename: NSString = "test_file.txt"
// Begin file manager segment
// Check for file presence and create it if it does not exist
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last?.appendingPathComponent(outFilename as String)
if !filemgr.fileExists(atPath: (path?.absoluteString)!) {
filemgr.createFile(atPath: String(outFilename), contents:Data(" ".utf8), attributes: nil)
}
// End file manager Segment
// Open outFilename for writing – this does not create a file
let fileHandle = FileHandle(forWritingAtPath: outFilename as String)
if(fileHandle == nil)
{
print("Open of outFilename forWritingAtPath: failed. \nCheck whether the file already exists. \nIt should already exist.\n");
exit(0)
}
var str: NSString = "2. Test string from NSString.\n";
var str0: String = "3. Test string from a Swift String.\n"
var str1: NSString = "4. Test string from NSString.\n";
fileHandle!.write("1. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write(String(str).data(using: .utf8)!)
fileHandle!.write(str0.data(using: .utf8)!)
fileHandle!.write(String(str1).data(using: .utf8)!)
fileHandle!.write("5. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write("6. Text in a loop follows: \n".data(using: .utf8)!)
for i in 0...5
{
//Assemble a string then write it to the file.
var s0: String = ""
s0 = String(i)
//s0.append(" ... some text here.\n") // See improvement below
s0 += " ... some text here.\n" // This is a better than .append
fileHandle!.write(s0.data(using: .utf8)!)
}
// End of file-writing segment
fileHandle!.closeFile()
This worked for me in Swift 5:
func writeFile() -> Bool
{
let outFilename: String = "test_file.txt"
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let outFilePath = documentsURL!.appendingPathComponent(outFilename).path
let fileManager = FileManager.default
// If file exists, remove it
if fileManager.fileExists(atPath: outFilePath)
{
do { try fileManager.removeItem(atPath: outFilePath) }
catch { return false }
}
// Create file and open it for writing
fileManager.createFile(atPath: outFilePath, contents:Data(" ".utf8), attributes: nil)
let fileHandle = FileHandle(forWritingAtPath: outFilePath)
if fileHandle == nil
{
return false
}
else
{
// Write data
fileHandle!.write("Test line 1\n".data(using: .utf8)!)
fileHandle!.write("Test line 2\n".data(using: .utf8)!)
fileHandle!.write("Test line 3\n".data(using: .utf8)!)
// Close file
fileHandle!.closeFile()
return true
}
}

Saving multiple lines of text in swift

I am attempting to store multiple lines of text in a local text file on an iphone. I have code which will create a text document, write data to that document and read data from this document.
However, if i try and add more text to this document, it will only store the most recent line of text which has been added.
The code I have for creating, writing and reading text from this document is as follows:
//Storing user rewards
let fileName = "Rewards"
let DocumentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = DocumentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
//print("File Path: \(fileURL.path)")
let writeString = rewardLBL.text
do {
//writing to the file
try writeString?.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8)
} catch let error as NSError{
print("failed to write")
print(error)
}
var readString = ""
do {
readString = try String(contentsOf: fileURL)
}catch let error as NSError{
print("failed to readFile")
print(error)
}
print(readString)
I need this to allow for multiple entries of text to be stored, rather than just the most recent data which was written.
I suspect that due to the code being inside the 'viewDidLoadi()' method that it is constantly recreating the same document and thus always making a new version which overwrites the old Rewards.txt document.
Any help is greatly appreciated, thanks.
Since you are using write, it will overwrite whatever is written earlier.
try writeString?.write(to: fileURL, atomically: true, encoding:
String.Encoding.utf8)
You need to append line of text to your file, which will not overwrite previous written lines. Something like this:
writeString.data(using: String.Encoding.utf8)?.write(to: fileURL, options: Data.WritingOptions.withoutOverwriting)

Converting Docx Files To Text In Swift

I have a .docx file in my temporary storage:
let location: NSURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
let file_Name = location.URLByAppendingPathComponent("5 November 2016.docx")
What I now want to do is extract the text inside this document. But I cannot seem to find any converters or methods of doing this.
I have tried this:
let file_Content = try? NSString(contentsOfFile: String(file_Name), encoding: NSUTF8StringEncoding)
print(file_Content)
However it prints nil.
So how do I read the text in a docx file?
Swift 4, Xcode 9.1, OSX targets from 10.10 to 10.13
I have found that the following code extracts text handily from a Word .doc file, which then easily goes into a string. (The attributed string contains formatting information that might be parsed to good effect.) The main info that I wanted to convey was the bit about using .docFormat to specify the document type.
let openPanel = NSOpenPanel()
var fileString = String("")
var fileData = NSData()
let fileURL = openPanel.url
do {
fileData = try NSData(contentsOf: fileURL!)
if let tryForString = try? NSAttributedString(data: fileData as Data, options: [
.documentType: NSAttributedString.DocumentType.docFormat,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil) {
fileString = tryForString.string
} else {
fileString = "Data conversion error."
}
fileString = fileString.trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
print("Word Document File Not Found")
}
Your initial issue is with how you get the string from the URL. String(File_Name) is not the correct way to convert a file URL into a file path. The proper way is to use the path function.
let location = NSURL.fileURLWithPath(NSTemporaryDirectory())
let fileURL = location.URLByAppendingPathComponent("My File.docx")
let fileContent = try? NSString(contentsOfFile: fileURL.path, encoding: NSUTF8StringEncoding)
Note the many changes. Use proper naming conventions. Name variables more clearly.
Now here's the thing. This still won't work because a docx file is a zipped up collection of XML and other files. You can't load a docx file into an NSString. You would need to use NSData to load the zip contents. Then you would need to unzip it. Then you would need to go through all of the files and find the desired text. It's far from trivial and it is far beyond the scope of a single stack overflow post.

Simple way to read local file using Swift?

I'm trying to learn the new Swift programming language. It looks great, but I'm having a difficult time doing something as simple as reading the content of a local .txt file.
I have tried the few examples I could find through Google, but they give compile errors, like this answer here: Read and write data from text file
If I tweak the code a bit, it works, but can only read from a special location within the project.
Why isn't it just as simple to read a .txt file with Swift as it is with for instance Ruby? And how would I go about reading the content of a file located at ~/file.txt?
Thnx
If you have a tilde in your path you can try this:
let location = "~/file.txt".stringByExpandingTildeInPath
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
otherwise just use this:
let location = "/Users/you/Desktop/test.txt"
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
This gives you a string representation of the file, which I assumed is what you want.
You can use NSData(contentsOfFile: location) to get a binary representation, but you would normally do that for, say, music files and not a text file.
Update: With Xcode 7 and Swift 2 this doesn't work anymore. You can now use
let location = NSString(string:"~/file.txt").stringByExpandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding)
let file = "/Users/user/Documents/text.txt"
let path=URL(fileURLWithPath: file)
let text=try? String(contentsOf: path)
This would work:
let path = "~/file.txt"
let expandedPath = path.stringByExpandingTildeInPath
let data: NSData? = NSData(contentsOfFile: expandedPath)
if let fileData = data {
let content = NSString(data: fileData, encoding:NSUTF8StringEncoding) as String
}
Note that data may be nil, so you should check for that.
EDIT:
Don't forget conditional unwrapping - looks much nicer ;)
Relative path tip:
Instead of doing this:
NSString("~/file.txt").stringByExpandingTildeInPath
You can do this:
"\(NSHomeDirectory())/file.txt"
You may find this tool useful to not only read from file in Swift but also parse it simultaneously: https://github.com/shoumikhin/StreamScanner
Just specify the file path and data delimiters like this (see readme for more options):
import StreamScanner
if let input = NSFileHandle(forReadingAtPath: "/file/path")
{
let scanner = StreamScanner(source: input, delimiters: NSCharacterSet(charactersInString: ":\n")) //separate data by colons and newlines
while let field: String = scanner.read()
{
//use field
}
}
Hope, this helps.
Using the answer by Atomix, this will work in Swift 4:
let location = NSString(string: "~/test.txt").expandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: String.Encoding.utf8.rawValue)
This worked for me in Swift 2.1, XCode7 to get the location and print the contents of CSV. ( you can create a simple CSV in Text Wrangler)
override func viewDidLoad() {
super.viewDidLoad()
let location = NSString(string:"/Users/*Myusername*/Documents/myCSVfile.csv")
let fileContent = try? NSString(contentsOfFile: location as String, encoding: NSUTF8StringEncoding)
print(fileContent)
}
Swift 4:
let filePath = "/Users/UserName/Desktop/FolderName/FileName.txt"
let fullPath = NSString(string: filePath).expandingTildeInPath
do
{
let fileContent = try NSString(contentsOfFile: fullPath, encoding: String.Encoding.utf8.rawValue)
print(fileContent)
}
catch
{
print(error)
}
filename doesn't need to have scheme like file://, and can be relative like ../docs/test.txt.
Remember to catch any error thrown, or rethrow.
let url = URL(fileURLWithPath: filename)
let contents = try String(contentsOf: url, encoding: .utf8)