After guard let url the value still needs to be unwrapped

1.3k Views Asked by At

In a singleton class I am trying the following code with 3 URLs stored in a dictionary:

class DownloadManager {
    static let instance = DownloadManager()
    
    let urls = [
        "en" : URL(string: "https://wordsbyfarber.com/ws/top"),
        "de" : URL(string: "https://wortefarbers.de/ws/top"),
        "ru" : URL(string: "https://slova.de/ws/top")
    ]
    
    var cancellables = Set<AnyCancellable>()

    private init() {
        getTops()
    }
    
    func getTops() {
        guard let url = urls["en"] else { return }
        
        URLSession.shared.dataTaskPublisher(for: url) // COMPILE ERROR
            .tryMap(handleOutput)
            .decode(type: TopResponse.self, decoder: JSONDecoder())
            .sink { completion in
                print(completion)
            } receiveValue: { fetchedTops in
                // ... store entities in Core Data
            }
            .store(in: &cancellables)
    }

But for some reason the line guard let url = urls["en"] else { return } is not sufficient to unwrap the value:

screenshot

Is this happening because the URL constructor might return nil?

Or because of the dictionary might not have a value for a key?

And why is guard statement not enough here?

3

There are 3 best solutions below

4
Sweeper On BEST ANSWER

urls is actually of type [String: URL?]. Note that the value type is optional, because URL.init(string:) is failable.

When you try to get a value from this dictionary, you get a URL??. The guard only unwraps one layer of the optional.

One way to unwrap a nested optional (no matter how many layers), is to use as?:

guard let url = urls["en"] as? URL else { return }
0
Gereon On

Is this happening because the URL constructor might return nil?

Yes.

let urls = [
    "en" : URL(string: "https://wordsbyfarber.com/ws/top"),
    "de" : URL(string: "https://wortefarbers.de/ws/top"),
    "ru" : URL(string: "https://slova.de/ws/top")
]

creates a dictionary with optional URLs ([String: URL?]), and your unwrapping only relates to the contents of the dictionary.

Use

let urls = [
    "en" : URL(string: "https://wordsbyfarber.com/ws/top")!,
    "de" : URL(string: "https://wortefarbers.de/ws/top")!,
    "ru" : URL(string: "https://slova.de/ws/top")!
]

for hardcoded URLs like this, or double-unwrap if it's unclear if the URLs are valid.

1
Shehata Gamal On

Thats as Url initializer returns an optional + the optional wrap of the Dictionary , If you're sure that all urls are valid then

 guard let url = urls["en"] else { return } 
 URLSession.shared.dataTaskPublisher(for: url!) // add only !

or only

 URLSession.shared.dataTaskPublisher(for: urls["en"]!!)   // add !!