После того, как URL-адрес guard let, значение все еще нужно развернуть

#swift #swift-guard

Вопрос:

В одноэлементном классе я пытаюсь выполнить следующий код с 3 URL-адресами, хранящимися в словаре:

 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: amp;cancellables)
    }
 

Но по какой-то причине строки guard let url = urls["en"] else { return } недостаточно, чтобы развернуть значение:

скриншот

Происходит ли это потому URL , что конструктор может вернуть ноль?

Или из-за того, что словарь может не иметь значения для ключа?

И почему здесь недостаточно заявления охраны?

Ответ №1:

urls на самом деле это тип [String: URL?] . Обратите внимание, что тип значения является необязательным, поскольку URL.init(string:) он может быть сбойным.

Когда вы пытаетесь получить значение из этого словаря, вы получаете URL?? . guard Единственный разворачивает один слой необязательного.

Один из способов развернуть вложенный необязательный элемент (независимо от количества слоев) — использовать as? :

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

Комментарии:

1. Ах, есть «слои опциональности»? Я все еще учусь быстрому

2. @AlexanderFarber Да, вы можете иметь URL? URL?? , URL??? и т. Д. Вы можете добавить произвольное количество вопросительных знаков в конце. Для URL?? , есть 3 случая, это может быть полностью ноль ( .none ), или внешний необязательный не равен нулю, но внутренний необязательный равен нулю ( .some(.none) ), или там может быть фактический URL ( .some(.some(http://example.com)) ).

3. @AlexanderFarber Эти случаи соответствуют «ошибка поиска по словарю», «инициализатор URL-адреса не удался, но поиск по словарю удался» и «оба успешно» соответственно.

4. @AlexanderFarber В опционах нет ничего особенного. Вы можете вложить их так же, как вы можете вложить массивы или словари в другие arrys или словари.

Ответ №2:

Происходит ли это из-за того, что конструктор URL может возвращать ноль?

ДА.

 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")
]
 

создает словарь с необязательными URL-адресами ( [String: URL?] ), и ваше разворачивание относится только к содержимому словаря.

Воспользуйся

 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")!
]
 

для жестко закодированных URL-адресов, подобных этому, или дважды разверните, если неясно, действительны ли URL-адреса.

Ответ №3:

Это как Url инициализатор возвращает необязательный необязательный перенос Dictionary , если вы уверены, что все URL-адреса действительны, то

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

или только

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

Комментарии:

1. @AlexanderFarber, возможно, но это приведет к путанице как для вас, так и для кого-то другого, если они будут читать ваш код позже