如何在计时器发布者触发时调用 URLSession 发布者

时间:2021-06-14 12:31:12

标签: swift combine

我想每 60 分钟调用一次基于计时器发布者的 URLSession 发布者。

这似乎应该是可能的,但不知道如何去做:

例如;

Client

2 个答案:

答案 0 :(得分:0)

class MyClass: ObservableObject {
    @Published var response: Response? = nil
    
    var cancelable : AnyCancellable?
    
    func connectPoller() {
        let timer = Timer.TimerPublisher(interval: 60 * 60, runLoop: .main, mode: .default)
            .autoconnect()  //<= here
        
        cancelable = timer
            .flatMap { _ in
                URLSession.shared.dataTaskPublisher(for: URL(string: "some-url-here")!).tryMap() { element -> Data in
                    guard let httpResponse = element.response as? HTTPURLResponse,
                          httpResponse.statusCode == 200 else {
                        throw URLError(.badServerResponse)
                    }
                    return element.data
                }
                .decode(type: Response?.self, decoder: JSONDecoder()).eraseToAnyPublisher()
                .replaceError(with: nil)
            }
            .receive(on: RunLoop.main) //<= here
            .assign(to: \.response, on: self)
    }
}

答案 1 :(得分:0)

为了将 Timer 发布者转换为 URLSession dataTask 发布者,您需要使用 flatMap。此外,您需要确保发布者链无法完成,因为计时器将在此时停止触发。因此,您需要对 flatMap 中的错误进行更积极的清理,以免它们进一步向下传播。

class MyClass: ObservableObject {
  @Published var response: Response? = nil
  var cancellable : AnyCancellable?
  func connectPoller() {
    cancellable = Timer.publish(every: 5, on: .main, in: .default)
        .autoconnect()
        .flatMap { _ in
            URLSession.shared.dataTaskPublisher(for: URL(string: "https://google.com")!)
                .flatMap { element -> AnyPublisher<Data,Never> in
                    guard let httpResponse = element.response as? HTTPURLResponse,
                                    httpResponse.statusCode == 200 else {
                        return Just(Data()).eraseToAnyPublisher()
                                    }
                    return Just(element.data).eraseToAnyPublisher()
                }
                .decode(type: Response?.self, decoder: JSONDecoder())
                .replaceError(with: nil)
        }
        .print()
        .receive(on: RunLoop.main)
        .assign(to: \.response, on: self)
    }
}