事件完成后,Swift Combine发布者将不会在flatMap中触发

时间:2020-10-30 18:46:02

标签: swift combine

我正在尝试在进行网络调用时使用CurrentValueSubject创建刷新事件。因此,每当网络请求失败时,我都可以按按钮再次提出请求,但由于失败事件将终止发布者,并且使其不再起作用,因此无法使其正常工作。

import Combine
import SwiftUI

struct ContentView: View {

    @ObservedObject var viewModel: TestViewModel = TestViewModel()

    var body: some View {
        Button("Test", action: viewModel.test)
        Button("Refresh", action: viewModel.refresh)
    }
}

class TestViewModel: ObservableObject {

    var bag = Set<AnyCancellable>()
    private let changeSubject = CurrentValueSubject<Void, Never>(())

    func test() {
        changeSubject
            .flatMap { self.networkPublisher }
            .sink(
                receiveCompletion: {
                    switch $0 {
                        case .failure:
                            print("Failure")
                        case .finished:
                            print("Finished")
                    }
                },
                receiveValue: {
                    print("Value: \($0)")
                }
            )
            .store(in: &bag)
    }

    func refresh() {
        changeSubject.send(())
    }

    var networkPublisher: AnyPublisher<String, Error> {
        var url = URLRequest(url: URLComponents(string: "www.google.com")!.url!)
        url.httpMethod = "GET"
        return URLSession.shared
            .dataTaskPublisher(for: url)
            .tryMap { _ -> String in "Result" }
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}

要解决此问题,我可以使用.tryCatch { _ -> Just<String> in Just("Error") }来捕获错误,这将阻止发布者终止。但是,为什么在发布者终止后它不起作用?比赛结束后我该如何工作?

2 个答案:

答案 0 :(得分:1)

不因错误而终止管道的模式是使用flatMap捕获错误:

changeSubject
   .flatMap {
      networkPublisher
         .catch { _ in Empty() }
   }
   .sink {
      print("Value: \($0)")
   }
   .store(in: &bag)

如您所见,.sink仅收到String的输出和Never的错误,因为错误是在.flatMap内完全处理的。

答案 1 :(得分:0)

您应该映射到新实例,而不是映射到发布者的单个实例。持久化的networkPublisher将触发其数据任务并完成操作,但是您将继续在flatMap中映射相同的完成的流。

MyService.exe install -username:DOMAIN\USER -password:xxxx start 应该是一个计算值,这样您每次通过都会返回一个新的发布者(和一个新的数据任务)。

from sklearn.metrics import mean_squared_error 

list_ = [[489066.76, 300334.], 
[227458.2,  200352.  ],
[928249.59, 946729.  ],
[339032.27, 350116.  ],
[689668.21, 600322.  ],
[489179.58, 577936.  ]]

y_true = [y[0] for y in list_]
y_pred = [y[1] for y in list_]

mse = mean_squared_error(y_true, y_pred)
print(mse)
# 8779930962.14985

def my_mse(y_true, y_pred):
  diff = 0
  for couple in zip(y_true, y_pred):
    diff+=pow(couple[0]-couple[1], 2)
  return diff/len(y_true)

print(my_mse(y_true, y_pred))
# 8779930962.14985

看看Apple的this sample code。数据发布者本身是在flatMap的闭包内创建的,它强调了每次都是新实例的事实。