-
-
Save stinger/7cb1a81facf7f846e3d53f60be34dd1e to your computer and use it in GitHub Desktop.
| import Foundation | |
| import Combine | |
| enum APIError: Error, LocalizedError { | |
| case unknown, apiError(reason: String) | |
| var errorDescription: String? { | |
| switch self { | |
| case .unknown: | |
| return "Unknown error" | |
| case .apiError(let reason): | |
| return reason | |
| } | |
| } | |
| } | |
| func fetch(url: URL) -> AnyPublisher<Data, APIError> { | |
| let request = URLRequest(url: url) | |
| return URLSession.DataTaskPublisher(request: request, session: .shared) | |
| .tryMap { data, response in | |
| guard let httpResponse = response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode else { | |
| throw APIError.unknown | |
| } | |
| return data | |
| } | |
| .mapError { error in | |
| if let error = error as? APIError { | |
| return error | |
| } else { | |
| return APIError.apiError(reason: error.localizedDescription) | |
| } | |
| } | |
| .eraseToAnyPublisher() | |
| } | |
| // Usage | |
| guard let url = URL(string: "https://www.amazon.com") else { return } | |
| fetch(url: url) | |
| .sink(receiveCompletion: { completion in | |
| switch completion { | |
| case .finished: | |
| break | |
| case .failure(let error): | |
| print(error.localizedDescription) | |
| } | |
| }, receiveValue: { data in | |
| guard let response = String(data: data, encoding: .utf8) else { return } | |
| print(response) | |
| }) |
Do you need to try fetch(url: url) at line 40?
I tried this but I am getting finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled". It got cancelled immediately after starting. Anyone has an idea why? I use a .print("test") and i am getting
test: receive subscription: (DataTaskPublisher)
test: request unlimited
test: receive cancel
same here, getting cancelled immediately after starting.
@workingDog I dont know what is the cause but I found the solution for this
Dont call .sink like that, declare a subscriber and call it
like this:
let subscriber = AnyCancellable?
subscriber.sink()
Thanks for the update.
It seemed an elegant way of fetching data. But after so much frigging around I
change tack and went with PromiseKit and the normal URLSession.
That works very well.
@junweimah The reason is that sink is returning an Cancellable. If this cancellable is not referenced it gets deallocated immediately and is calling cancel. If you hold a reference to it this is not the case.
let sub = fetch(url: url)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}, receiveValue: { data in
guard let response = String(data: data, encoding: .utf8) else { return }
print(response)
})Should work here
It does not work when you encapsulate the fetch inside a function and have the Cancellable let sub reside on the same scope because the completion is called long after the function is destroyed!
It is necessary to have a global Cancellable optional variable available that survives the entire scope of the caller of fetch!
@junweimah The reason is that
sinkis returning an Cancellable. If this cancellable is not referenced it gets deallocated immediately and is calling cancel. If you hold a reference to it this is not the case.let sub = fetch(url: url) .sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): print(error.localizedDescription) } }, receiveValue: { data in guard let response = String(data: data, encoding: .utf8) else { return } print(response) })Should work here
Thanks, I've been looking for a good
tryMapandmapErrorwithURLSession.DataTaskPublisherexample for awhile!