【问题标题】:Force publisher to publish an update?强制发布者发布更新?
【发布时间】:2021-03-14 06:54:52
【问题描述】:

在我的ViewModel 的init() 中,我有 2 个订阅者。

  1. 第一个订阅DataProvider() 中的下载数据并使用结果更新downloadedData。这会进行 API 调用,通常需要几秒钟才能发布数据。
  2. 第二个订阅$savedData,然后仅使用savedData 中包含的数据更新filteredData 数组AND downloadedData。

问题是downloadedData 需要时间来下载,而savedData 是即时的(它实际上在Core Data 中)。下载后,我想强制更新上面的第二个观察者($savedData)以相应地更新filteredData。

有没有办法手动强制发布者发布一个值?在 ObservedObjects 上,我们可以调用 objectWillChange.send(),但我可以调用 $savedData 吗?

免责声明:这是我的应用程序的超级简化版本。请不要包含改变层次结构的答案。我还意识到我可以在 $allCoins 上添加另一个发布者来模仿 $savedData 上的发布者,但我宁愿只强制更新 $savedData。谢谢!

    import SwiftUI
    
    struct TestView: View {
        
        @StateObject var vm = ViewModel()
        
        
        var body: some View {
            VStack {
                ForEach(vm.filteredData, id: \.self) { user in
                    Text(user)
                }
            }
        }
    }
    
    struct TestView_Previews: PreviewProvider {
        static var previews: some View {
            TestView()
        }
    }
    
    import Combine
    
    class ViewModel: ObservableObject {
        
        @Published var downloadedData: [String] = []
        @Published var savedData: [String] = []
        @Published var filteredData: [String] = []
        var cancellables = Set<AnyCancellable>()
        @Published var provider = DataProvider()
    
        init() {
            // WHEN THIS GETS PUBLISHED...
            // CAN I FORCE A PUBLISH / UPDATE TO savedData?
            provider.$data
                .assign(to: \.downloadedData, on: self)
                // here?
                .store(in: &cancellables)
            
            $savedData
                .map { (saved) -> [String] in
                    return saved.map { (savedItem) -> String in
                        return self.downloadedData.contains(savedItem) ? savedItem : "NO"
                    }
                }
                .assign(to: \.filteredData, on: self)
                .store(in: &cancellables)
            
            getSavedData()
        }
        
        func getSavedData() {
            savedData = ["ONE", "TWO"]
        }
        
    }
    
    class DataProvider: ObservableObject {
        
        @Published var data: [String] = []
        
        init() {
            getData()
        }
        
        func getData() {
            // simulating api call
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.data.append(contentsOf: ["ONE", "TWO"])
            }
        }
    }

【问题讨论】:

  • 正如其他答案所述,assign(to:on:) 可能会导致内存泄漏。但是,从 iOS 14 开始,您可以使用安全的 assign(to:)。

标签: swift swiftui combine


【解决方案1】:

您可能以错误的方式思考问题。

首先,让我们正确理解术语。您正在订阅您的init 中的 2 个发布者。每当它们各自的属性发生变化时,这两个发布者都会发出一个值。

您似乎想要做的是在savedData 或downloadedData 更改时更新filteredData。因此,与其“强制更新”,不如订阅那种在你想要的时候发出你想要的东西的发布者。您可以使用CombineLatest 来实现:

$savedData.combineLatest(provider.$data)
   .map { (saved, downloaded) in
      //...
   }
   .sink { [weak self] value in
      self?.filteredData = value
   }
   .store(in: &cancellables)

(不要使用assign - 它会创建对self 的强引用;请改用sink 和weak self)

【讨论】:

  • Yesssssssssssssssssssssssssssssssssssssssssssssssssss!这就是我一直在寻找的。谢谢!
  • Apple 在 iOS 14 中引入了assign(to:),可以安全使用。所以你可以做.assign(to: &amp; filteredData)。
【解决方案2】:

您可以订阅下载的数据并将其分配给已保存的数据:

$downloadedData
    .assign(to: \.savedData, on: self)
    .store(in: &cancellables)

当self作为参数传递时,使用assign(to: on:)会导致内存泄漏,请参考以下链接。

https://forums.swift.org/t/does-assign-to-produce-memory-leaks/29546/11

您的其他订阅也调用assign(to: on:),并以self 作为参数,最好在链接中实施该解决方案,因为它可以在整个应用程序中解决此问题。

【讨论】:

  • 感谢您的回复,但这不是我要问的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多