简体   繁体   中英

Swift Impossible Type Inference

I am trying to load a file from my app bundle in Swift 3, and I came across a weird situation with the Swift type inferencing. If I use the following code, I get an error on the third line that says Value of optional type "String?" not unwrapped Value of optional type "String?" not unwrapped .

let url = NSURL(fileURLWithPath:Bundle.main.bundlePath)
let url2 = url.appendingPathComponent("foo.txt")
let path:String = url2?.path

To fix the error I unwrap the value on the third line by changing it to:

let path:String = url2?.path!

I now get the error Cannot force unwrap value of a non-optional type 'String' . It seems like Swift can't determine whether the path property is a String or a String? . The autocomplete feature in Xcode says it is a String , but the docs say it is a String? .

The suggested fix by Xcode for the first error was to replace url2?.path with (url2?.path)! , which finally ended up working, but I have no idea why this works and the other ways don't.

let path:String = (url2?.path)!

What is going on? Is this a type inference bug in Swift, or am I missing something super obvious

In Swift, Optional chaining like:

let path:String = url2?.path!

... is interpreted as:

let path:String = url2 != nil ? url2!.path!
                              : nil

As you see the type of path is non-Optional String , so the expression causes error.

( url2 's type is URL? , so the type of property path is String , not String? .)


This is not a direct answer to your question, but I would re-write your code as:

let url = Bundle.main.bundleURL
let url2 = url.appendingPathComponent("foo.txt")
let path:String = url2.path

Shorter, and no worry about Optionals.

You forgot to unwrap url2

appendingPathComponent returns an optional value and you are trying to access it without unwrapping it.

So,

let url2 = url.appendingPathComponent("foo.txt")!

or

guard let url2 = url.appendingPathComponent("foo.txt") else { }

should fix it

EDIT

let path:String? = url2?.path

works also

You can also do this:

let url = Bundle.main.url(forResource: "foo", withExtension: "txt")!
let path:String = url.path

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM