URLSession.shared struct property isn't working for Combine API call

87 Views Asked by At

I have following APILoader struct in my network layer.

struct APILoader<T: APIHandler> {
    
    var apiHandler: T
    var urlSession: URLSession
    
    init(apiHandler: T, urlSession: URLSession = .shared) {
        self.apiHandler = apiHandler
        self.urlSession = urlSession
    }

func loadAPIRequest(requestData: T.RequestDataType) -> AnyPublisher<T.ResponseDataType, Error> {
        
        guard let urlRequest = apiHandler.makeRequest(from: requestData) else {
            return Result<T.ResponseDataType, Error>.failure(NSError(domain: "", code: -1, userInfo: ["dte":1])).publisher.eraseToAnyPublisher()
        }
        
        return urlSession.dataTaskPublisher(for: urlRequest)
            .map() {
                $0.data
            }
            .decode(type: T.ResponseDataType.self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
}
}

When I use urlSession struct property to call dataTaskPublisher(for: urlRequest) method, it's not working. But this is working for non combine traditional API calls. When I use instance variable of URLSession.shared like follows, it's start working.

return URLSession.shared.dataTaskPublisher(for: urlRequest)
.map() {
                $0.data
            }
            .decode(type: T.ResponseDataType.self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()

Following code shows how I init the APILoader and call the web service.

func getData() {
        let request = TopStoriesService()
        let params = [Params.kApiKey.rawValue : CommonUtil.shared.NytApiKey()]
        let apiLoader = APILoader(apiHandler: request)
        apiLoader.loadAPIRequest(requestData: params)
            .sink(receiveCompletion: { _ in },
                  receiveValue: { res in
                print("\(res)")
            })
            
    }

Can anyone explain what I'm missing here?

1

There are 1 best solutions below

0
burnsi On

You are not holding on to your publisher. This code:

apiLoader.loadAPIRequest(requestData: params)
    .sink(receiveCompletion: { _ in },
          receiveValue: { res in
        print("\(res)")
    })

does return an AnyCancellable that needs to be stored. For now you closure just goes out of scope. So where ever this function lives that object needs to store a reference to your AnyCancellable:

// in class or struct scope
var cancellable: AnyCancellable?

// in your function
cancellable = apiLoader.loadAPIRequest(requestData: params)
    .sink(receiveCompletion: { _ in },
          receiveValue: { res in
        print("\(res)")
    })

But:

as your example code does not include a reproducible example there might be more going on.