Почему URLSession.DataTaskPublisher никогда не публикует значения?

В Xcode 11 beta 5 или 6 мой существующий код, который полагался на URLSession.DataTaskPublisher, перестал работать. Кажется, что DataTaskPublisher никогда не публикует никаких значений, но я не могу понять почему.

Я пробовал с .sink и .handleEvents в качестве подписчиков. Я протестировал .sink с Just издателем и подтвердил, что он там получает значение.

Я также пробовал дать DataTaskPublisher URL и URLRequest. Я пробовал запрос к API, включая заголовок авторизации, а также базовые запросы к google.com и apple.com. Я пробовал использовать URLSession.shared и создать новый экземпляр URLSession. Я также пробовал с операторами map и decode и без них.

Я использовал XCTest ожидания, чтобы подтвердить, что время ожидания теста истекает каждый раз, даже если я даю ему 4-минутный тайм-аут.

Я только что создал новый пример проекта и воспроизвел проблему со следующим кодом в корневом контроллере представления:

override func viewDidLoad() {
        super.viewDidLoad()

        print("view did load")

        URLSession.shared.dataTaskPublisher(for: URL(string: "http://apple.com")!)
            .handleEvents(receiveSubscription: { (sub) in
                print(sub)
            }, receiveOutput: { (response) in
                print(response)
            }, receiveCompletion: { (completion) in
                print(completion)
            }, receiveCancel: {
                print("cancel")
            }, receiveRequest: { (demand) in
                print(demand)
            })
    }

Проект печатает «вид загружен», но больше ничего не печатает. Есть идеи о том, где я здесь ошибаюсь? Спасибо!


person Belle B. Cooper    schedule 31.08.2019    source источник
comment
avanderlee.com/debugging/combine-swift может дать несколько советов. Печать и уловка кажутся полезными.   -  person Michael Salmon    schedule 31.08.2019
comment
@matt - перенаправления URL-адресов должны быть в порядке, но я также пробовал с другими URL-адресами и имел ту же проблему со всеми из них, так что, похоже, это не проблема. Тем не менее, спасибо за предложение.   -  person Belle B. Cooper    schedule 02.09.2019


Ответы (1)


Я думаю, что с вашим кодом есть две проблемы: во-первых, у вас есть только издатель (handleEvent возвращает издателя), а во-вторых, издатель выходит за рамки и исчезает. Это работает, хотя и не совсем элегантно.


import Combine
import SwiftUI

var pub: AnyPublisher<(data: Data, response: URLResponse), URLError>? = nil
var sub: Cancellable? = nil

var data: Data? = nil
var response: URLResponse? = nil

func combineTest() {
    guard let url = URL(string: "https://apple.com") else {
        return
    }
    pub = URLSession.shared.dataTaskPublisher(for: url)
            .print("Test")
            .eraseToAnyPublisher()
    sub = pub?.sink(
        receiveCompletion: { completion in
            switch completion {
            case .finished:
                break
            case .failure(let error):
                fatalError(error.localizedDescription)
            }
        },
        receiveValue: { data = $0.data; response = $0.response }
    )
}

struct ContentView: View {
    var body: some View {
        Button(
            action: { combineTest() },
            label: { Text("Do It").font(.largeTitle) }
        )
    }
}

Я сделал это в SwiftUI, чтобы не о чем беспокоиться, и использовал 3 переменные, чтобы лучше следить. Вам нужно использовать приемник с двумя параметрами, так как ошибка издателя - это не Never. Наконец, print () предназначен только для тестирования и работает очень хорошо.

person Michael Salmon    schedule 01.09.2019
comment
Сказать, что издатель выходит за рамки, было не совсем правильно. Он никогда не входил в сферу охвата, поскольку никогда не был назначен. Извините. - person Michael Salmon; 01.09.2019
comment
Спасибо за это! Как я уже упоминал, я попытался использовать sink, но это все равно не сработало, но вы помогли мне найти основную проблему, которая заключалась в том, что мне нужно сохранить ссылку на подписчика. Я попытался создать для него локальную переменную, но мне нужно было создать свойство класса для подписчика, чтобы оно не выпадало из области видимости. Большое спасибо за вашу помощь, это сводит меня с ума! Изменить: для ясности, сохраняя ссылку на подписчика в классе, но не сохраняя ссылку на работу издателя. Так что ссылка на издателя кажется необязательной. - person Belle B. Cooper; 02.09.2019
comment
Извините, что я этого не прояснил. В исходном коде издатель исчез в облаке дыма, но важно сохранить подписчика. Я считаю, что в деинитах отменяемых элементов есть отмена, которая разрушает цепочку. Я обнаружил, что если у вас несколько подписчиков, то при любой отмене останавливаются все подписчики, возможно, для этого и предназначена многоадресная рассылка. - person Michael Salmon; 02.09.2019