从文本文件中读取和写入字符串

我需要从一个文本文件读取和写入数据,但我还没有弄清楚怎么做。

我在Swift的iBook中找到了这个示例代码,但我仍然不知道如何写入或读取数据。

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."
520702 次浏览

假设你已经将你的文本文件data.txt移动到你的Xcode-project(使用拖放并勾选“必要时复制文件”),你可以像在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
< p > 更新:
对于从Bundle (iOS)读取文件,你可以使用:

let path = NSBundle.mainBundle().pathForResource("FileName", ofType: "txt")
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)

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)!

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)

对于读写,您应该使用一个可写的位置,例如文档目录。下面的代码展示了如何读写一个简单的字符串。你可以在操场上测试。

斯威夫特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 */}
}

斯威夫特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 */}
}

快1.倍

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)
}

目前从亚当上面接受的答案对我来说有一些错误,但这是我如何重做他的答案,使这个工作对我来说。

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)")
}
}

Xcode 8。3. Swift;X或以上

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)
}

写入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 []
}

为了避免混淆并增加易用性,我创建了两个函数,用于向documents目录中的文件读取和写入字符串。功能如下:

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
}

下面是一个使用它们的例子:

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.'

希望这能有所帮助!

Xcode版本:6.3.2

你可能会发现这个工具不仅可以从Swift中读取文件,还可以解析你的输入:https://github.com/shoumikhin/StreamScanner

只需要像这样指定文件路径和数据分隔符:

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
}
}

希望这有帮助。

新的更简单和推荐的方法: 苹果建议使用url进行文件处理,这里的其他解决方案似乎已被弃用(见下面的评论)。 下面是读取和写入URL的新简单方法:

Swift 5+, 4和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)")

我必须像这样重新编码:

let path = NSBundle.mainBundle().pathForResource("Output_5", ofType: "xml")
let text = try? NSString(contentsOfFile: path! as String, encoding: NSUTF8StringEncoding)
print(text)

在函数示例中,(read|write)DocumentsFromFile(…)拥有一些函数包装器似乎是有意义的,因为OSx和iOS中的所有东西似乎都需要实例化三到四个主要类和一堆属性,配置,链接,实例化和设置,只是为了将“Hi”写入一个文件,在182个国家。

但是,这些示例还不够完整,无法在实际程序中使用。写入函数不会报告任何创建或写入文件的错误。在读取时,我认为返回文件不存在的错误不是一个好主意,因为字符串应该包含被读取的数据。您可能希望通过某种通知机制(比如异常)知道它失败了,以及失败的原因。然后,您可以编写一些代码,输出问题是什么,并允许用户纠正它,或者“正确地”在此时中断程序。

您不会希望只返回一个包含“错误文件不存在”的字符串。然后,您必须在每次调用函数时查找字符串中的错误并在那里处理它。您也可能无法真正判断错误字符串实际上是从实际文件中读取的,还是从代码中生成的。

在swift 2.2和Xcode 7.3中,你甚至不能像这样调用read,因为NSString(contentsOfFile…)会抛出异常。这是一个编译时错误,如果你没有任何代码来捕捉它,并对它做一些事情,如打印到标准输出,或更好的,一个错误弹出窗口,或标准错误。我听说苹果正在远离try catch和异常,但这将是一个漫长的过程,没有它就不可能编写代码。我不知道error参数从哪里来,也许是一个旧版本,但是NSString。writeTo[File|URL]当前没有NSError参数。它们在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

此外,文件不存在只是程序读取文件时可能遇到的许多潜在问题之一,例如权限问题、文件大小问题或许多其他问题,您甚至不想尝试为每个问题编写处理程序。最好只是假设它都是正确的,并在出现错误时捕获、打印或处理异常,此外,在这一点上,您实际上没有选择。

以下是我的改写:

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
}

我只想给你看第一部分,即。以下是简单的阅读方法:

斯威夫特3:

let s = try String(contentsOfFile: Bundle.main.path(forResource: "myFile", ofType: "txt")!)

斯威夫特2:

let s = try! String(contentsOfFile: NSBundle.mainBundle().pathForResource("myFile", ofType: "txt")!)

对于我的txt文件是这样工作的:

let myFileURL = NSBundle.mainBundle().URLForResource("listacomuni", withExtension: "txt")!
let myText = try! String(contentsOfURL: myFileURL, encoding: NSISOLatin1StringEncoding)
print(String(myText))

Xcode 8, Swift 3从应用程序包中读取文件的方法:

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)")
}

这是一个方便的复制和粘贴扩展

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 ""
}
}

例如

let t = "yourFile.txt".contentsOrBlank()

你几乎总是想要一个行数组:

let r:[String] = "yourFile.txt"
.contentsOrBlank()
.characters
.split(separator: "\n", omittingEmptySubsequences:ignore)
.map(String.init)
 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
}
< p > 最新swift3代码 < br > 你可以从文本文件读取数据,只需使用下面的代码 这是我的文本文件

     {
"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)"
}]

你可以使用这段代码从swift3的文本json文件中获取数据

     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)
}

这适用于Linux上的Swift 3.1.1:

import Foundation


let s = try! String(contentsOfFile: "yo", encoding: .utf8)

Xcode 8.3.2 Swift 3.x。使用NSKeyedArchiver和NSKeyedUnarchiver

从文档中读取文件

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")
}

将文件写入文档

NSKeyedArchiver.archiveRootObject(finalDataDict, toFile:(jsonFilePath?.absoluteString)!)

在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")}
}

早期的解决方案回答了问题,但在我的情况下,删除文件的旧内容而写入是一个问题。

所以,我创建了一段代码,用于在文档目录中写入文件,而不删除以前的内容。您可能需要更好的错误处理,但我相信这是一个很好的起点。斯威夫特4。 Usuage: < / p >
    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)
}

辅助方法:

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
}
}

斯威夫特3。X - 5.x

最好的例子是创建一个扩展名为.txt的本地Logfile 可以在"Files App"中以当前日期和时间作为文件名显示

只需在信息中添加此代码。请启用这两个功能

  UIFileSharingEnabled
LSSupportsOpeningDocumentsInPlace

和下面的函数

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

建议异步读写文件!这在纯Swift中很容易做到,
下面是协议:

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)
}

正如您所看到的,它允许您同步或异步地读取和写入文件。

以下是我在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)
}