0

Trying to write data from UI to IOS File Storage :

 @IBAction func savetoFile(sender: AnyObject) {
        let csvLine:String="\(bookName.text), \(bookAuthor.text),\(bookIsbn.text)\n"
        let paths = NSSearchPathForDirectoriesInDomains(
            NSSearchPathDirectory.DocumentDirectory,
            NSSearchPathDomainMask.UserDomainMask, true)
        let docDir:String=paths[0] as String
        let bookFile:String=(docDir as NSString).stringByAppendingPathComponent("bookresults.dat")

        if !NSFileManager.defaultManager().fileExistsAtPath(bookFile) {
            NSFileManager.defaultManager().createFileAtPath(bookFile,
                contents: nil, attributes: nil)
        }

        let fileHandle:NSFileHandle=NSFileHandle(forUpdatingAtPath:bookFile)!
        fileHandle.seekToEndOfFile()
        fileHandle.writeData(csvLine.dataUsingEncoding(NSUTF8StringEncoding)!)
        fileHandle.closeFile()

Problem :

If I give "BookOne" , program writes to file as "Optional("BookOne") and repeats this "Optional" for every field , every row.

Appreciate if someone could share the fix with code for this problem.

Tech stack is Xcode 7.1 and SWIFT.

Thanks

  • 1
    That is because, the 'text' field is optional on your bookName, bookAuthor, bookIsBn, etc. Try unwrapping them, and you should be good. – Shripada Nov 04 '15 at 03:22
  • 1
    Thanks Shripada .. I appended every text field with a '!' to unwrap and it fixed the problem . – Arches Arches Nov 04 '15 at 03:39
  • 2
    Appending ! is not a good idea. It is a crash waiting to happen. See http://stackoverflow.com/a/33399973/239816 – Paul Cantrell Nov 04 '15 at 03:54

1 Answers1

1

If foo is an optional type (String?, for example), then string interpolation puts that annoying Optional(...) wrapper around it. (That’s a language design mistake IMO — interpolation should either unwrap automatically or not accept optionals — but Apple did it the way they did it!)

To get rid of the Optional(...), you need to make the thing inside the \() not be optional. There are many ways of doing that.

One is to use default values:

"\(bookName.text ?? ""), \(bookAuthor.text ?? ""), etc"

(This assumes you want nil to come out as an empty string.)

The expression bookName.text ?? "" cannot evaluate to nil, so its type is String and not String?, and the Optional(...) wrapper goes away. You could even put that in a helper:

func emptyIfNil(string: String?) -> String {
    return string ?? ""
}

"\(emptyIfNil(bookName.text)), \(emptyIfNil(bookAuthor.text)), etc"

Another approach is to use if let to unwrap the optionals:

if let name = bookNext.text, let author = bookAuthor.text, ...etc... {
    let csvLine:String="\(name), \(author),\(isbn)\n"
    ...
}

This would only output rows where all the values are present.

A tidy approach in your case is to use sequence operations, and avoid interpolation altogether:

let csvLine = [bookName, bookAuthor, bookIsbn]
    .map { $0.text ?? "" }
    .joinWithSeparator(",")

I strongly recommend against using ! to unwrap the optionals in this (and most) situations. Saying bookName.text! means, “if bookName.text is nil, that is such a terribly calamity that the best thing to do is to crash the whole app.” If there is any reasonable behavior for the case when bookName.text is nil — and I imagine in your case there is — then you should code for it instead of allowing the possibility of that crash.

Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48