2
votes

I'm trying to understand the Combine methodology of making a JSON network call. I'm clearly missing something basic.

The closest I get fails with the URLSession cancelled.

class NoteDataStore: ObservableObject {

    @Published var notes: [MyNote] = []

    init() {
        getWebserviceNotes()
    }

    func getWebserviceNotes() {

        let pub = Webservice().fetchNotes()
            .sink(receiveCompletion: {_ in}, receiveValue: { (notes) in
            self.notes = notes
            })
        }
    }
}//class

The data element:

struct MyNote: Codable, Identifiable {
    let id = UUID()
    var title: String
    var url: String
    var thumbnailUrl: String

    static var placeholder: MyNote {
        return MyNote(title: "No Title", url: "", thumbnailUrl: "")
    }
}

The network setup:

class Webservice {
    func fetchNotes() -> AnyPublisher<[MyNote], Error> {
        let url = "https://jsonplaceholder.typicode.com/photos"
        guard let notesURL = URL(string: url) else { fatalError("The URL is broken")}

        return URLSession.shared.dataTaskPublisher(for: notesURL)
            .map { $0.data }
            .decode(type: [MyNote].self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
    }
}

The console output is:

Task <85208F00-BC24-44AA-B644-E0398FE263A6>.<1> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://jsonplaceholder.typicode.com/photos, NSLocalizedDescription=cancelled, NSErrorFailingURLKey=https://jsonplaceholder.typicode.com/photos}

Any guidance would be appreciated. Xcode 11.4

2

2 Answers

2
votes
let pub = Webservice().fetchNotes()

this publisher is released on exit of scope, so make it member, like

private var publisher: AnyPublisher<[MyNote], Error>?

func getWebserviceNotes() {
    self.publisher = Webservice().fetchNotes()
    ...
1
votes

Based on Asperi's answer - you will also want to add:

var cancellable: AnyCancellable?

And then you can sink to get the data:

func getWebserviceNotes() {

    self.publisher = Webservice().fetchNotes()
    guard let pub = self.publisher else { return }

    cancellable = pub
            .sink(receiveCompletion: {_ in },
                  receiveValue: { (notes) in
                    self.notes = notes
            })
}