Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read file in swift, iOS playground

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
like image 315
alttag Avatar asked Oct 23 '14 22:10

alttag


2 Answers

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)
}
like image 132
Leo Dabus Avatar answered Nov 13 '22 22:11

Leo Dabus


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

like image 31
Steve Rosenberg Avatar answered Nov 13 '22 22:11

Steve Rosenberg