Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't URLSession.DataTaskPublisher ever publish values?

In Xcode 11 beta 5 or 6 my existing code that relied on URLSession.DataTaskPublisher stopped working. It seems like DataTaskPublisher is never publishing any values but I can't work out why.

I've tried with .sink and .handleEvents as subscribers. I've tested .sink with a Just publisher and confirmed it receives a value there.

I've also tried both giving the DataTaskPublisher a URL and giving it a URLRequest. I've tried a request to an API including an authorization header, as well as basic requests to google.com and apple.com. I've tried using URLSession.shared and creating a new instance of URLSession. I've also tried with and without map and decode operators.

I've used XCTest expectations to confirm that the test times out every single time, even if I give it a 4-minute timeout.

I just made a new example project and replicated the problem with the following code in the root view controller:

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)
            })
    }

The project prints "view did load" but nothing else ever prints. Any ideas about where I'm going wrong here? Thanks!

like image 475
Belle B. Cooper Avatar asked Aug 31 '19 07:08

Belle B. Cooper


People also ask

What is Datataskpublisher?

A publisher that delivers the results of performing URL session data tasks.

What is URLSession in Swift?

Overview. The URLSession class and related classes provide an API for downloading data from and uploading data to endpoints indicated by URLs. Your app can also use this API to perform background downloads when your app isn't running or, in iOS, while your app is suspended.

What is URLSession shared?

For basic requests, the URLSession class provides a shared singleton session object that gives you a reasonable default behavior for creating tasks. Use the shared session to fetch the contents of a URL to memory with just a few lines of code.

How do I create a URLSession in Swift?

Before we start, we need to define the URL of the remote image. import UIKit let url = URL(string: "https://bit.ly/2LMtByx")! The next step is creating a data task, an instance of the URLSessionDataTask class. A task is always tied to a URLSession instance.


1 Answers

I think that there are two problems with your code, firstly you only have a publisher (handleEvent returns a publisher) and secondly that publisher goes out of scope and disappears. This works although it isn't exactly elegant.


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) }
        )
    }
}

I did it in SwiftUI so that I would have less to worry about and I used 3 variables so that I could follow better. You need to use the 2 parameter sink as the publisher's error isn't Never. Finally the print() is just for test and works really well.

like image 124
Michael Salmon Avatar answered Sep 27 '22 22:09

Michael Salmon