Swift: can't load file using String(contentsOfFile:encoding:) - swift

Seems like it should be simple. There are examples all over the place using String(contentsOfFile:encoding:), but none of them work for me. The main problem is that when I catch the exception, I can't figure out what exception it is. The doc speaks of exception codes, but it doesn't say what enum they belong to.
The file is a plain text file, I know it's there, and I have permissions to it. A lot of the Apple doc says that there's an inout error parameter that you can pass in, but that doc is clearly neglected by Apple, sorely outdated, and inconsistent with both itself and the compiler.
How can I figure out what the error is, or, is there a better way to load a text file than with String(contentsOfFile:encoding:)?

This has worked for me.
let path = Bundle.main.path(forResource: "myFile", ofType: "txt", inDirectory: "")
do {
var myString = try String(contentsOfFile: path!, encoding: .utf8)
// do something with myString
} catch {
//error
}

Related

Can we use "guard" instead of let <var> = try? <action> in swift?

What is the difference between:
guard let json_data = Data(contentsOf: path) else {return nil}
and
let json_data = try? Data(contentsOf: path)
I dont want to use optional while loading the data into the variable. I want other ways to try it.
Thanks in advance.
The options are:
Your first example, unwrapping it with guard, is missing a try?:
func foo() -> Bar? {
guard let jsonData = try? Data(contentsOf: path) else { return nil }
// if you get here, `jsonData` is not `Optional`
…
}
This will safely unwrap your optional and let you do whatever you want if the unwrapping failed. (In your example, you are returning nil.)
Your second example, yields an optional, which you presumably need to unwrap with an if statement.
func foo() -> Bar? {
let jsonData = try? Data(contentsOf: path)
// jsonData is `Optional` in this example
if let jsonData {
…
} else {
return nil
}
}
We would generally favor the first option over this, where the “early exit” of the guard makes it a little easier to read the code, but there are cases where you might use this pattern.
An option that hasn’t been considered is to actually throw the error (using try instead of try?):
func foo() throws -> Bar {
let jsonData = try Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this only passes the buck of handling the error to the caller (i.e., a do-catch block). But it does have a few virtues over the prior two examples, namely that (a) the useful information of the error object is not just discarded, thereby making it easier to diagnose problems during the development process; and (b) you don’t have to return an optional.
Yet another option (to be used only if you know that this will always succeed, e.g., you are reading a well-known file from your bundle that you know must always succeed) is try!, a “force-try”:
func foo() -> Bar {
let jsonData = try! Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this will crash if the Data(contentsOf:) can ever fail, so only use this in scenarios where you know that this is impossible.
Personally, I would generally favor option 3 (where I capture what went wrong) in cases where the Data(contentsOf:) might ever plausibly fail at runtime, and I might consider option 4 (where it crashes with a meaningful error message) when I know it is impossible for it to ever fail at runtime. That having been said, more than once I found myself using option 4 and I later regretted not using option 3, simply because there was some weird edge-case that I neglected to consider.
In short, nowadays I tend to defensively catch errors, log the full error in the console and show a nice localized message in the UI (i.e., option 3). I almost never use try?, because if something can fail, it’s rarely a good idea to discard the useful diagnostic information.
It will be better to be optional once you are loading the data to avoid application crash in and problems in case there was no data there. This is consider as a safe feature.

Integrating ML Model in Xcode's Swift Playgrounds App

So I am using Xcode Swift Playground App. In which I want to add my two MLModels (Validator and Classifier).
At first, I created a dummy xcode project and I copied the Swift Model Class file. But still, I am getting this error in bundle.url
The issue is that you are force-unwrapping a variable (which is not a very good practice).
The force-unwrapping is failing, because there is not a resource with the same name and path that you are attempting to use.
Instead of force-unwrapping, I would suggest something like this:
class var urlofModelInThisBundle : URL f
let bundle = Bundle(for: self)
if let resource = bundle.url(forResource: "XrayValidator2", withExtension:"mlmodelc") {
return resource
}
else {
Print("ERR: attempted to use a resource that was not found")
return nil
}
You can control-click on your resource names, choose "Reveal in finder" to verify the full names of your resources.
EDIT:
To list all files in the bundle run this and please paste the output into your question:
let paths = Bundle.main.paths(forResourcesOfType: "mlmodelc", inDirectory: nil)
print(paths)
If you don't have any results from that, try this:
let paths = Bundle.main.paths(forResourcesOfType: "mlmodel", inDirectory: nil)
print(paths)

Use the try? operator to make the code more concise

I have this code in Swift 2 to move a file to a new destination, overwriting if necessary:
let origin = "...", destination = "..."
do {
try NSFileManager.defaultManager().removeItemAtPath(destination) // remove existing file
} catch {}
do {
try NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
} catch {}
To make the code more concise, and since I don't care about the error thrown, I thought of using the try? operator as follows:
let origin = "...", destination = "..."
try? NSFileManager.defaultManager().removeItemAtPath(destination)
try? NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
This creates a compiler warning that the result of the operation is being unused, so I have to add an unused let and it looks awful:
...
let _ = try? NSFileManager.defaultManager().moveItemAtPath(origin, toPath: destination)
Is it bad to let the warnings there in the sake of conciseness?
In Swift 3 it is legal to ignore the result without the extra assignment. This compiles just fine, with no warning, in the Xcode 8 GM:
try? FileManager.default.removeItem(atPath: destination)
(Earlier, by the way, I asked about this on bugs.swift.org, and was told directly that this _ = try?
syntax is regarded as correct and is a small price to pay for acknowledging to the compiler — and yourself — that you are deliberately ignoring the returned value. So what you are doing is just fine while you remain in the Swift 2 world!)
Note that you have these warnings because you are calling some method and you do not handle returning value.
If you do not need returning value then you should use let _ = syntax to explicitly say to everyone else who reads this code that returning value is ignored intentionally.
Otherwise if you should handle returning value but you did not do that yet because you are too lazy or by some other reason then it's considered to be a bug in your software and you definitely should not suppress it.

How to determine file URL from DVTSourceTextView

I’m writing a plugin to Xcode 7. I have the DVTSourceTextView and can manipulate it just fine. One of the things I want to find is which file is related to this. Unfortunately, DVTSourceTextView doesn’t appear to offer that information - or if it does, it is buried in a way I fail to see.
I’m sure it is rather trivial, I’m just missing something.
Okay, this was easier than I thought it was. I was approaching it from a different (although almost correct) way.
class func currentEditorView() -> (NSURL?, NSView?) {
let currentWindowController = NSApp.keyWindow?.windowController
guard currentWindowController!.className == "IDEWorkspaceWindowController" else { return (nil, nil) }
let filename = currentWindowController!.valueForKey("editorArea")!.valueForKey("lastActiveEditorContext")!.valueForKey("originalRequestedDocumentURL")
let editor = currentWindowController!.valueForKey("editorArea")!.valueForKey("lastActiveEditorContext")!.valueForKey("editor")!.valueForKey("textView")
return (filename as? NSURL, editor as? NSView)
}
This gives me both the filename as an NSURL as well as the DVTSourceTextView as an NSView without the need of including private headers. Spiffy.
Now not only do I know the name of the file I’m editing, but I can also determine if it is a swift, objc, c or c++ file! THAT is coolness!

Is there any way to use an immutable outside of a try block in Swift?

I know that if I want to use the variable text outside of this try block in Swift, I would write this code thusly,
var text = NSString()
do {
text = try NSString( contentsOfURL: url, encoding: NSUTF8StringEncoding ) }
catch let errOpening as NSError {
// ...
}
From days of yore when storage was meted by bytes, my mantra has been to use constants if at all possible. So in the instance where text will be loaded once and never changed, my gut tells me to make it a constant.
do {
let text = try NSString( contentsOfURL: url, encoding: NSUTF8StringEncoding ) }
catch let errOpening as NSError {
// ...
}
But then I can't use the loaded text outside of the try block. Is there any way to have text be treated as a constant outside of the try block in this context by Swift? Or is this just the yearning of an old man coding in an old style, and I should use var, forget about it, and move on?
Many thanks in advance!
You can do:
let text: String
do {
text = try String(contentsOfURL: url, encoding: NSUTF8StringEncoding)
}
catch let error as NSError {
// ...
}
(I’ve used String, the native string type in Swift, rather than NSString.)
Assuming later code makes use of text, the catch block must either assign something to it or return from the enclosing function, as constants must be initialised before being used.
Bear in mind that the method you’re using to retrieve the contents of a URL as a string does so synchronously, so if this code runs on the main thread you will block the UI. Look up the NSURLSession docs for details on asynchronously loading the content of URLs, which will avoid blocking the UI.