Swift writing to contents of textview to RTF file doesn't work XCODE 12.4 - swift

I've been trying all day to write contents of a textview into an already existing RTF file in my project. However, it doesn't work. It compiles successfully but when it's all done running, and I go to the file, nothing I'm writing is in the rtf file. here is the code:
if let rtfPath = Bundle.main.url(forResource: "BugReports", withExtension: "rtf") {
if let attributedText = reportEntry1.attributedText {
let documentAttributes: [NSAttributedString.DocumentAttributeKey: Any] = [.documentType: NSAttributedString.DocumentType.rtf]
do {
let text2 = try String(contentsOf: rtfPath, encoding: .utf8)
print(text2)
let rtfData = try attributedText.data(from: NSRange(location: 0, length: attributedText.length), documentAttributes: documentAttributes)
let rtfString = String(data: rtfData, encoding: .utf8) ?? ""
try rtfString.write(to: rtfPath, atomically: true, encoding: .utf8)
} catch {
print("failed to submit a report: \(error)")
}
}
}
EDIT, I now know bundles cannot be written to so this is what it is currently, but it still doesn't work.
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let rtfPath = dir.appendingPathComponent("BugReports.rtf")
if let attributedText = reportEntry1.attributedText {
let documentAttributes: [NSAttributedString.DocumentAttributeKey: Any] = [.documentType: NSAttributedString.DocumentType.rtf]
do {
let rtfData = try attributedText.data(from: NSRange(location: 0, length: attributedText.length), documentAttributes: documentAttributes)
let rtfString = String(data: rtfData, encoding: .utf8) ?? ""
try rtfString.write(to: rtfPath, atomically: true, encoding: .utf8)
} catch {
print("failed to submit a reportaaaaaaa: \(error)")
}
}
}

Related

Save NSTextView content with formatting and images inserted

I am trying to save the content of a NSTextView item containing text, images, and different formatting as bold, italic, different font size and color. Currently I am only able to save the raw text but not the images nor the formatting.
let fileURL = savePath.appendingPathComponent("savedText.txt")
if !fileManager.fileExists(atPath: fileURL.path) {
do {
try fileManager.createDirectory(at: savePath, withIntermediateDirectories: true, attributes: nil)
fileManager.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
print("File created :", fileURL)
} catch {
print("Failed to save text to file:", error)
}
}
do {
try textView.string.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Failed to save the file")
}
I tried saving the file as rtf or rtfd but it's only saving the raw content.
Edit : I found how to save and load the rich text but still now way to save the images pasted in the text view. If I make a file .rtfd with an image it loads so the problem is clearly somewhere in the save function. Here is my new code to save :
// Save the text to a file whenever it changes
let fileURL = savePath
// if the file doesn't exist make one
if !fileManager.fileExists(atPath: fileURL.path) {
do {
try fileManager.createDirectory(at: savePath, withIntermediateDirectories: true, attributes: nil)
fileManager.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
print("File created :", fileURL)
} catch {
print("Failed to save text to file:", error)
}
}
// Backup the old file
do {
try fileManager.copyItem(at: fileURL.appendingPathComponent("savedText.rtfd"), to: fileURL.appendingPathComponent("savedText.rtfd.old"))
} catch {
print("Error copying file: \(error)")
}
// Save the file
let image = NSImage(named: "MyImage")
let attachment = NSTextAttachment()
attachment.image = image
let attributedString = NSMutableAttributedString(attributedString: textView.attributedString())
let attachmentString = NSAttributedString(attachment: attachment)
attributedString.append(attachmentString)
let fileWrapper = FileWrapper(directoryWithFileWrappers: [:])
let rtfData = attributedString.rtf(from: NSRange(location: 0, length: attributedString.length))
if let rtfData = rtfData {
fileWrapper.addRegularFile(withContents: rtfData, preferredFilename: "savedText.rtfd")
}
do {
try fileWrapper.write(to: fileURL, options: .atomic, originalContentsURL: nil)
} catch {
print("Faild to save the file : \(error)")
}
Here is how I finally made it :
I used from the NSTextView.attributedString() the function (rtfdFileWrapper(from: NSRange)) to extract the rich text and images as a rtfd File Wrapper. After you have to save it with the write function of the FileWrapper
let fileURL = savePath.appendingPathComponent(fileName)
// Save the file
let attributedString = textView.attributedString()
let rtfdData = attributedString.rtfdFileWrapper(from: NSRange(location: 0, length: attributedString.length))
if let rtfdData = rtfdData {
rtfdData.filename = "savedText.rtfd"
do {
try rtfdData.write(to: fileURL, options: .atomic, originalContentsURL: savePath)
} catch {
print("Faild to write \(error)")
}
}

How to export a text file as a PDF file (macOS Swift)?

So I'm building a macOS app and I would like it to save or export a copy of this text file the app creates as a PDF file as well. I'm a beginner and I'm not sure how to proceed.
Note: I tried to change .txt to .pdf but the PDF cannot be opened due to incorrect file formatting.
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let docURL = URL(string: documentsDirectory)!
let dataPath = docURL.appendingPathComponent("User_Notes") // this is the folder name
let stringToWrite =
"""
User: Smith John Apple
Today's Date: 07/11/2021
This is a a note that the user writes in the app.
"""
if !FileManager.default.fileExists(atPath: dataPath.path) || FileManager.default.fileExists(atPath: dataPath.path) {
do {
try
FileManager.default.createDirectory(atPath: dataPath.path, withIntermediateDirectories: true, attributes: nil)
try stringToWrite.write(toFile: "\(dataPath/\(dateField.stringValue)/\(userName.stringValue))).txt", atomically: true, encoding: String.Encoding.utf8)
} catch {
print(error.localizedDescription)
}
}
if error != nil {
print("Error saving user data.")
}
}
This is what the text file looks like:
This is what I would like the PDF to look like:
An easy way to create PDF data from a string is by using a NSTextView.
Example:
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = urls[0]
let dataURL = docURL.appendingPathComponent("User_Notes")
let stringToWrite =
"""
User: Smith John Apple
Today's Date: 07/11/2021
This is a a note that the user writes in the app.
"""
let txtData = Data(stringToWrite.utf8)
// create a NSTextView
let textView = NSTextView(frame: NSRect(x: 0, y: 0, width: 612, height: 791))
textView.textContainerInset = NSSize(width: 50, height: 50)
// add font attribute to the string and insert in the NSTextView
if let font = NSFont(name: "Menlo", size: 11) {
let attributedString = NSAttributedString(string: stringToWrite, attributes: [.font: font])
textView.insertText(attributedString, replacementRange: NSRange(location: 0, length: 0))
}
else {
textView.insertText(stringToWrite, replacementRange: NSRange(location: 0, length: 0))
}
// generate pdf data
let pdfData = textView.dataWithPDF(inside: textView.bounds)
// write data to files
do {
try FileManager.default.createDirectory(at: dataURL, withIntermediateDirectories: true, attributes: nil)
let documentName = "\(dateField.stringValue)_\(userName.stringValue)"
try txtData.write(to: dataURL.appendingPathComponent("\(documentName).txt"), options: .atomic)
try pdfData.write(to: dataURL.appendingPathComponent("\(documentName).pdf"), options: .atomic)
} catch {
print(error.localizedDescription)
}

How To Read From File Swift [duplicate]

I need to read and write data to/from a text file, but I haven't been able to figure out how.
I found this sample code in the Swift's iBook, but I still don't know how to write or read data.
import Cocoa
class DataImporter {
/*
DataImporter is a class to import data from an external file.
The class is assumed to take a non-trivial amount of time to initialize.
*/
var fileName = "data.txt"
// the DataImporter class would provide data importing functionality here
}
class DataManager {
#lazy var importer = DataImporter()
var data = String[]()
// the DataManager class would provide data management functionality here
}
let manager = DataManager()
manager.data += "Some data"
manager.data += "Some more data"
// the DataImporter instance for the importer property has not yet been created”
println(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// prints "data.txt”
var str = "Hello World in Swift Language."
For reading and writing you should use a location that is writeable, for example documents directory. The following code shows how to read and write a simple string. You can test it on a playground.
Swift 3.x - 5.x
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 {/* error handling here */}
//reading
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
}
catch {/* error handling here */}
}
Swift 2.2
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 = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = NSURL(fileURLWithPath: dir).URLByAppendingPathComponent(file)
//writing
do {
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
}
catch {/* error handling here */}
//reading
do {
let text2 = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
}
catch {/* error handling here */}
}
Swift 1.x
let file = "file.txt"
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
let dir = dirs[0] //documents directory
let path = dir.stringByAppendingPathComponent(file);
let text = "some text"
//writing
text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
//reading
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
}
Assuming that you have moved your text file data.txt to your Xcode-project (Use drag'n'drop and check "Copy files if necessary") you can do the following just like in Objective-C:
let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("data", ofType: "txt")
let content = NSString.stringWithContentsOfFile(path) as String
println(content) // prints the content of data.txt
Update:
For reading a file from Bundle (iOS) you can use:
let path = NSBundle.mainBundle().pathForResource("FileName", ofType: "txt")
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)
Update for Swift 3:
let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
For Swift 5
let path = Bundle.main.path(forResource: "ListAlertJson", ofType: "txt") // file path for file "data.txt"
let string = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
Xcode 8.x • Swift 3.x or later
do {
// get the documents folder url
if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
// create the destination url for the text file to be saved
let fileURL = documentDirectory.appendingPathComponent("file.txt")
// define the string/text to be saved
let text = "Hello World !!!"
// writing to disk
// Note: if you set atomically to true it will overwrite the file if it exists without a warning
try text.write(to: fileURL, atomically: false, encoding: .utf8)
print("saving was successful")
// any posterior code goes here
// reading from disk
let savedText = try String(contentsOf: fileURL)
print("savedText:", savedText) // "Hello World !!!\n"
}
} catch {
print("error:", error)
}
New simpler and recommended method:
Apple recommends using URLs for filehandling and the other solutions here seem deprecated (see comments below).
The following is the new simple way of reading and writing with URL's:
Swift 5+, 4 and 3.1
import Foundation // Needed for those pasting into Playground
let fileName = "Test"
let dir = try? FileManager.default.url(for: .documentDirectory,
in: .userDomainMask, appropriateFor: nil, create: true)
guard let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("txt") else {
fatalError("Not able to create URL")
}
// Writing to the file named Test
let outString = "Write this text to the file"
do {
try outString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
assertionFailure("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
// Reading it back from the file
var inString = ""
do {
inString = try String(contentsOf: fileURL)
} catch {
assertionFailure("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("Read from the file: \(inString)")
Xcode 8, Swift 3 way to read file from the app bundle:
if let path = Bundle.main.path(forResource: filename, ofType: nil) {
do {
let text = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
print(text)
} catch {
printError("Failed to read text from \(filename)")
}
} else {
printError("Failed to load file from app bundle \(filename)")
}
Here's a convenient copy and paste Extension
public extension String {
func contentsOrBlank()->String {
if let path = Bundle.main.path(forResource:self , ofType: nil) {
do {
let text = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
return text
} catch { print("Failed to read text from bundle file \(self)") }
} else { print("Failed to load file from bundle \(self)") }
return ""
}
}
For example
let t = "yourFile.txt".contentsOrBlank()
You almost always want an array of lines:
let r:[String] = "yourFile.txt"
.contentsOrBlank()
.characters
.split(separator: "\n", omittingEmptySubsequences:ignore)
.map(String.init)
I want to show you only the first part, that is read. Here's how simply you can read:
Swift 3:
let s = try String(contentsOfFile: Bundle.main.path(forResource: "myFile", ofType: "txt")!)
Swift 2:
let s = try! String(contentsOfFile: NSBundle.mainBundle().pathForResource("myFile", ofType: "txt")!)
Simplest way to read a file in Swift > 4.0
let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
do {
var text = try String(contentsOfFile: path!)
}
catch(_){print("error")}
}
You may find this tool useful to not only read from file in Swift but also parse your input: https://github.com/shoumikhin/StreamScanner
Just specify the file path and data delimiters like this:
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.
This works with Swift 3.1.1 on Linux:
import Foundation
let s = try! String(contentsOfFile: "yo", encoding: .utf8)
The current accepted answer above from Adam had some errors for me but here is how I reworked his answer and made this work for me.
let file = "file.txt"
let dirs: [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if (dirs != nil) {
let directories:[String] = dirs!
let dirs = directories[0]; //documents directory
let path = dirs.stringByAppendingPathComponent(file);
let text = "some text"
//writing
text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
//reading
var error:NSError?
//reading
let text2 = String(contentsOfFile: path, encoding:NSUTF8StringEncoding, error: &error)
if let theError = error {
print("\(theError.localizedDescription)")
}
}
To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions:
func writeToDocumentsFile(fileName:String,value:String) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
let path = documentsPath.stringByAppendingPathComponent(fileName)
var error:NSError?
value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding, error: &error)
}
func readFromDocumentsFile(fileName:String) -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
let path = documentsPath.stringByAppendingPathComponent(fileName)
var checkValidation = NSFileManager.defaultManager()
var error:NSError?
var file:String
if checkValidation.fileExistsAtPath(path) {
file = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil) as! String
} else {
file = "*ERROR* \(fileName) does not exist."
}
return file
}
Here is an example of their use:
writeToDocumentsFile("MyText.txt","Hello world!")
let value = readFromDocumentsFile("MyText.txt")
println(value) //Would output 'Hello world!'
let otherValue = readFromDocumentsFile("SomeText.txt")
println(otherValue) //Would output '*ERROR* SomeText.txt does not exist.'
Hope this helps!
Xcode Version: 6.3.2
I had to recode like this:
let path = NSBundle.mainBundle().pathForResource("Output_5", ofType: "xml")
let text = try? NSString(contentsOfFile: path! as String, encoding: NSUTF8StringEncoding)
print(text)
In the function example, (read|write)DocumentsFromFile(...) having some function wrappers certainly seems to makes sense since everything in OSx and iOS seems to need three or four major classes instantiated and a bunch of properties, configured, linked, instantiated, and set, just to write "Hi" to a file, in 182 countries.
However, these examples aren't complete enough to use in a real program. The write function does not report any errors creating or writing to the file. On the read, I don't think it's a good idea to return an error that the file doesn't exist as the string that is supposed to contain the data that was read. You would want to know that it failed and why, through some notification mechanism, like an exception. Then, you can write some code that outputs what the problem is and allows the user to correct it, or "correctly" breaks the program at that point.
You would not want to just return a string with an "Error file does not exist" in it. Then, you would have to look for the error in the string from calling function each time and handle it there. You also possibly couldn't really tell if the error string was actually read from an actual file, or if it was produced from your code.
You can't even call the read like this in swift 2.2 and Xcode 7.3 because NSString(contentsOfFile...) throws an exception. It is a compile time error if you do not have any code to catch it and do something with it, like print it to stdout, or better, an error popup window, or stderr. I have heard that Apple is moving away from try catch and exceptions, but it's going to be a long move and it's not possible to write code without this. I don't know where the &error argument comes from, perhaps an older version, but NSString.writeTo[File|URL] does not currently have an NSError argument. They are defined like this in NSString.h :
public func writeToURL(url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws
public func writeToFile(path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws
public convenience init(contentsOfURL url: NSURL, encoding enc: UInt) throws
public convenience init(contentsOfFile path: String, encoding enc: UInt) throws
Also, the file not existing is just one of a number of potential problems your program might have reading a file, such as a permissions problem, the file size, or numerous other issues that you would not even want to try to code a handler for each one of them. It's best to just assume it's all correct and catch and print, or handle, an exception if something goes amiss, besides, at this point, you don't really have a choice anyway.
Here are my rewrites :
func writeToDocumentsFile(fileName:String,value:String) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString!
let path = documentsPath.stringByAppendingPathComponent(fileName)
do {
try value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding)
} catch let error as NSError {
print("ERROR : writing to file \(path) : \(error.localizedDescription)")
}
}
func readFromDocumentsFile(fileName:String) -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let path = documentsPath.stringByAppendingPathComponent(fileName)
var readText : String = ""
do {
try readText = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
}
catch let error as NSError {
print("ERROR : reading from file \(fileName) : \(error.localizedDescription)")
}
return readText
}
For my txt file works this way:
let myFileURL = NSBundle.mainBundle().URLForResource("listacomuni", withExtension: "txt")!
let myText = try! String(contentsOfURL: myFileURL, encoding: NSISOLatin1StringEncoding)
print(String(myText))
Latest swift3 code
You can read data from text file just use bellow code
This my text file
{
"NumberOfSlices": "8",
"NrScenes": "5",
"Scenes": [{
"dataType": "label1",
"image":"http://is3.mzstatic.com/image/thumb/Purple19/v4/6e/81/31/6e8131cf-2092-3cd3-534c-28e129897ca9/mzl.syvaewyp.png/53x53bb-85.png",
"value": "Hello",
"color": "(UIColor.red)"
}, {
"dataType": "label2",
"image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
"value": "Hi There",
"color": "(UIColor.blue)"
}, {
"dataType": "label3",
"image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
"value": "hi how r u ",
"color": "(UIColor.green)"
}, {
"dataType": "label4",
"image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
"value": "what are u doing ",
"color": "(UIColor.purple)"
}, {
"dataType": "label5",
"image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
"value": "how many times ",
"color": "(UIColor.white)"
}, {
"dataType": "label6",
"image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/5a/f3/06/5af306b0-7cac-1808-f440-bab7a0d18ec0/mzl.towjvmpm.png/53x53bb-85.png",
"value": "hi how r u ",
"color": "(UIColor.blue)"
}, {
"dataType": "label7",
"image":"http://is5.mzstatic.com/image/thumb/Purple71/v4/a8/dc/eb/a8dceb29-6daf-ca0f-d037-df9f34cdc476/mzl.ukhhsxik.png/53x53bb-85.png",
"value": "hi how r u ",
"color": "(UIColor.gry)"
}, {
"dataType": "label8",
"image":"http://is2.mzstatic.com/image/thumb/Purple71/v4/15/23/e0/1523e03c-fff2-291e-80a7-73f35d45c7e5/mzl.zejcvahm.png/53x53bb-85.png",
"value": "hi how r u ",
"color": "(UIColor.brown)"
}]
}
You can use this code you get data from text json file in swift3
let filePath = Bundle.main.path(forResource: "nameoftheyourjsonTextfile", ofType: "json")
let contentData = FileManager.default.contents(atPath: filePath!)
let content = NSString(data: contentData!, encoding: String.Encoding.utf8.rawValue) as? String
print(content)
let json = try! JSONSerialization.jsonObject(with: contentData!) as! NSDictionary
print(json)
let app = json.object(forKey: "Scenes") as! NSArray!
let _ : NSDictionary
for dict in app! {
let colorNam = (dict as AnyObject).object(forKey: "color") as! String
print("colors are \(colorNam)")
// let colour = UIColor(hexString: colorNam) {
// colorsArray.append(colour.cgColor)
// colorsArray.append(colorNam as! UIColor)
let value = (dict as AnyObject).object(forKey: "value") as! String
print("the values are \(value)")
valuesArray.append(value)
let images = (dict as AnyObject).object(forKey: "image") as! String
let url = URL(string: images as String)
let data = try? Data(contentsOf: url!)
print(data)
let image1 = UIImage(data: data!)! as UIImage
imagesArray.append(image1)
print(image1)
}
It is recommended to read and write files asynchronously! and it's so easy to do in pure Swift,
here is the protocol:
protocol FileRepository {
func read(from path: String) throws -> String
func readAsync(from path: String, completion: #escaping (Result<String, Error>) -> Void)
func write(_ string: String, to path: String) throws
func writeAsync(_ string: String, to path: String, completion: #escaping (Result<Void, Error>) -> Void)
}
As you can see it allows you to read and write files synchronously or asynchronously.
Here is my implementation in Swift 5:
class DefaultFileRepository {
// MARK: Properties
let queue: DispatchQueue = .global()
let fileManager: FileManager = .default
lazy var baseURL: URL = {
try! fileManager
.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("MyFiles")
}()
// MARK: Private functions
private func doRead(from path: String) throws -> String {
let url = baseURL.appendingPathComponent(path)
var isDir: ObjCBool = false
guard fileManager.fileExists(atPath: url.path, isDirectory: &isDir) && !isDir.boolValue else {
throw ReadWriteError.doesNotExist
}
let string: String
do {
string = try String(contentsOf: url)
} catch {
throw ReadWriteError.readFailed(error)
}
return string
}
private func doWrite(_ string: String, to path: String) throws {
let url = baseURL.appendingPathComponent(path)
let folderURL = url.deletingLastPathComponent()
var isFolderDir: ObjCBool = false
if fileManager.fileExists(atPath: folderURL.path, isDirectory: &isFolderDir) {
if !isFolderDir.boolValue {
throw ReadWriteError.canNotCreateFolder
}
} else {
do {
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
} catch {
throw ReadWriteError.canNotCreateFolder
}
}
var isDir: ObjCBool = false
guard !fileManager.fileExists(atPath: url.path, isDirectory: &isDir) || !isDir.boolValue else {
throw ReadWriteError.canNotCreateFile
}
guard let data = string.data(using: .utf8) else {
throw ReadWriteError.encodingFailed
}
do {
try data.write(to: url)
} catch {
throw ReadWriteError.writeFailed(error)
}
}
}
extension DefaultFileRepository: FileRepository {
func read(from path: String) throws -> String {
try queue.sync { try self.doRead(from: path) }
}
func readAsync(from path: String, completion: #escaping (Result<String, Error>) -> Void) {
queue.async {
do {
let result = try self.doRead(from: path)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
}
func write(_ string: String, to path: String) throws {
try queue.sync { try self.doWrite(string, to: path) }
}
func writeAsync(_ string: String, to path: String, completion: #escaping (Result<Void, Error>) -> Void) {
queue.async {
do {
try self.doWrite(string, to: path)
completion(.success(Void()))
} catch {
completion(.failure(error))
}
}
}
}
enum ReadWriteError: LocalizedError {
// MARK: Cases
case doesNotExist
case readFailed(Error)
case canNotCreateFolder
case canNotCreateFile
case encodingFailed
case writeFailed(Error)
}
write in ViewDidLoad
var error: NSError?
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var documentsDirectory = paths.first as String
var dataPath = documentsDirectory.stringByAppendingPathComponent("MyFolder")
if !NSFileManager.defaultManager().fileExistsAtPath(dataPath) {
NSFileManager.defaultManager().createDirectoryAtPath(dataPath, withIntermediateDirectories: false, attributes: nil, error: &error)
} else {
println("not creted or exist")
}
func listDocumentDirectoryfiles() -> [String] {
if let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as? String {
let myFilePath = documentDirectory.stringByAppendingPathComponent("MyFolder")
return NSFileManager.defaultManager().contentsOfDirectoryAtPath(myFilePath, error: nil) as [String]
}
return []
}
func writeToDocumentsFile(fileName:String,value:String) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path = documentsPath.appendingPathComponent(fileName)
do{
try value.write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
}catch{
}
}
func readFromDocumentsFile(fileName:String) -> String {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
let path = documentsPath.appendingPathComponent(fileName)
let checkValidation = FileManager.default
var file:String
if checkValidation.fileExists(atPath: path) {
do{
try file = NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue) as String
}catch{
file = ""
}
} else {
file = ""
}
return file
}
Earlier solutions answers question, but in my case deleting old content of file while writing was a problem.
So, I created piece of code for writing to file in documents directory without deleting previous content. You probably need better error handling, but I believe it's good starting point. Swift 4.
Usuage:
let filename = "test.txt"
createOrOverwriteEmptyFileInDocuments(filename: filename)
if let handle = getHandleForFileInDocuments(filename: filename) {
writeString(string: "aaa", fileHandle: handle)
writeString(string: "bbb", fileHandle: handle)
writeString(string: "\n", fileHandle: handle)
writeString(string: "ccc", fileHandle: handle)
}
Helper methods:
func createOrOverwriteEmptyFileInDocuments(filename: String){
guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
debugPrint("ERROR IN createOrOverwriteEmptyFileInDocuments")
return
}
let fileURL = dir.appendingPathComponent(filename)
do {
try "".write(to: fileURL, atomically: true, encoding: .utf8)
}
catch {
debugPrint("ERROR WRITING STRING: " + error.localizedDescription)
}
debugPrint("FILE CREATED: " + fileURL.absoluteString)
}
private func writeString(string: String, fileHandle: FileHandle){
let data = string.data(using: String.Encoding.utf8)
guard let dataU = data else {
debugPrint("ERROR WRITING STRING: " + string)
return
}
fileHandle.seekToEndOfFile()
fileHandle.write(dataU)
}
private func getHandleForFileInDocuments(filename: String)->FileHandle?{
guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
debugPrint("ERROR OPENING FILE")
return nil
}
let fileURL = dir.appendingPathComponent(filename)
do {
let fileHandle: FileHandle? = try FileHandle(forWritingTo: fileURL)
return fileHandle
}
catch {
debugPrint("ERROR OPENING FILE: " + error.localizedDescription)
return nil
}
}
Swift 3.x - 5.x
The Best Example is to Create a Local Logfile with an Extension .txt
that can visible and show in the "Files App" with current date and Time as a File Name
just add this code in info.plist enable these two features
UIFileSharingEnabled
LSSupportsOpeningDocumentsInPlace
and this Function Below
var logfileName : String = ""
func getTodayString() -> String{
let date = Date()
let calender = Calendar.current
let components = calender.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date)
let year = components.year
let month = components.month
let day = components.day
let hour = components.hour
let minute = components.minute
let second = components.second
let today_string = String(year!) + "-" + String(month!) + "-" + String(day!) + "-" + String(hour!) + "" + String(minute!) + "" + String(second!)+".txt"
return today_string
}
func LogCreator(){
logfileName = getTodayString()
print("LogCreator: Logfile Generated Named: \(logfileName)")
let file = logfileName //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)
let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)[0]
print("LogCreator: The Logs are Stored at location \(documentPath)")
//writing
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */}
//reading
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
print("LogCreator: The Detail log are :-\(text2)")
}
catch {/* error handling here */}
}
}
[1]: https://i.stack.imgur.com/4eg12.png
Xcode 8.3.2 Swift 3.x. Using NSKeyedArchiver and NSKeyedUnarchiver
Reading file from documents
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("Filename.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
if fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {
let finalDataDict = NSKeyedUnarchiver.unarchiveObject(withFile: (jsonFilePath?.absoluteString)!) as! [String: Any]
}
else{
print("File does not exists")
}
Write file to documents
NSKeyedArchiver.archiveRootObject(finalDataDict, toFile:(jsonFilePath?.absoluteString)!)

Writing Log Text File-Should I create an Array to store Filenames or Keep the File Open [duplicate]

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)

Decode base64 to binary file in swift

How can I decode base64 binary file to the file in swift?like docx
I used this code but return always nil.
if let decodedData = NSData(base64EncodedString:"base64string" , options:.Ig(rawValue: 0)),
let decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding) {
print(decodedString)
}
You are supposed to pass a base 64-string into the NSData-initializer ("base64string" is not a valid base64, hence the initializer returns nil).
let stringToEncode = "testString"
let stringData = stringToEncode.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = stringData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
if let decodedData = NSData(base64EncodedString:base64String , options: .IgnoreUnknownCharacters),
let decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding) {
print(decodedString)
}
let myString = "my data"
Encoding
let plainData = (myString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromRaw(0)!)
println(base64String)
Decoding
if let decodedData = NSData(base64EncodedString: base64String, options:NSDataBase64DecodingOptions.fromRaw(0)!) {
let decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding)
println(decodedString)
}
In Swift 4, you can do base64 encoding or decoding like this:
extension Data {
init?(base64String: String) {
self.init(base64Encoded: base64String)
}
var base64String: String {
return self.base64EncodedString()
}
}
extension String {
init?(base64String: String) {
guard let data = Data(base64String: base64String) else {
return nil
}
self.init(data: data, encoding: .utf8)
}
var base64String: String {
return self.data(using: .utf8)!.base64String
}
}