多次 onReceive 使用会导致重复的 API 调用

Posted

技术标签:

【中文标题】多次 onReceive 使用会导致重复的 API 调用【英文标题】:Multiple onReceive usages lead to duplicated API calls 【发布时间】:2021-07-22 08:13:35 【问题描述】:

我们开始在我们的应用程序中使用 Combine for Networks 调用。用户应该输入一个代码,我们希望通过网络调用验证该代码,然后根据所述网络调用的结果执行操作。

据我们了解,如果我们有一个已发布的字符串变量并使用它来懒惰地创建另一个发布者来进行 API 调用,那么我们只会在每次字符串更改时进行 API 调用。

但是,通过onReceive 监听发布者的 SwiftUI 视图越多,调用的 API 就越多。这是一个简化的示例:

查看

struct ContentView: View 

    @ObservedObject var viewModel: ResultBenchmarkViewModel

    @State var one: String = "ONE"
    @State var two: String = "TWO"
    @State var three: String = "THREE"

    var body: some View 
        VStack 
        
            TextField("INPUT", text: $viewModel.inputString)
        
            Text(one)
                .onReceive(viewModel.inputStringPublisher, perform:  string in
                    one = string
                )
        
            Text(two)
                .onReceive(viewModel.inputStringPublisher, perform:  string in
                    two = string
                )
        
            Text(three)
                .onReceive(viewModel.inputStringPublisher, perform:  string in
                    three = string
                )
        
    

视图模型

class ResultBenchmarkViewModel: Identifiable, ObservableObject 

    @Published var inputString: String = ""

    lazy var inputStringPublisher: AnyPublisher<String, Never> = 
    
        return $inputString
            .map  string in
                print("API CALL")
                return string
            
            .eraseToAnyPublisher()
    ()

使用onReceive 三次也会在用户每次输入字符时打印三次“API CALL”。我们预计会发生的是,“API CALL”只会打印一次,因为inputString 只会更改一次,然后使用onReceive 的三个视图只会收到发布者发布的最终结果。

我们现在正在实施的一个解决方案是监听 inputString,进行 api 调用并将结果保存到本地发布的变量中,然后视图会根据该变量进行更新。但是,对我来说,这似乎是样板文件,如果我们正确实施原始请求,可能会过时。

我们能否通过对实现进行一些小的更改来实现我们的预期结果?还是这里对 combine/swiftui 有根本的误解?

【问题讨论】:

【参考方案1】:

您可以“共享”发布者的输出。这就是 Share Publisher 的用途。

https://developer.apple.com/documentation/combine/fail/share()

使用 Share Publisher,您的 inputStringPublisher 将如下所示:

lazy var inputStringPublisher: AnyPublisher<String, Never> = 

    return $inputString
        .map  string in
            print("API CALL")
            return string
        
        .share()
        .eraseToAnyPublisher()
()

就个人而言,我会继续使用已发布的变量解决方案,因为视图模型中的逻辑更多。

【讨论】:

非常感谢!做到了。我们将讨论保留哪种解决方案,但最好了解共享运算符。【参考方案2】:

您需要将.share 修饰符添加到您的inputStringPublisher

return $inputString
  .map  string in
    print("API CALL")
    return string
  
  .share()
  .eraseToAnyPublisher()

【讨论】:

非常感谢!我很遗憾只能接受一个答案,而另一张海报出现在你面前。但你的答案也是解决方案。 没问题!这是几秒钟的事情哈哈。很高兴你找到了解决方案:)。

以上是关于多次 onReceive 使用会导致重复的 API 调用的主要内容,如果未能解决你的问题,请参考以下文章

onReceive 调用了很多次

防止多次引入js文件导致的重复注册点击事件

SwiftUI 视图中的 onReceive 导致无限循环

事件委托导致重复绑定多次事件

使用Struts 2防止表单重复提交

避免表单重复提交的几种方法