【问题标题】:Converting Key-Value Observation in AppKit/UIKit to Combine and SwiftUI将 AppKit/UIKit 中的 Key-Value Observation 转换为 Combine 和 SwiftUI
【发布时间】:2021-02-14 06:43:09
【问题描述】:

我在思考如何在 SwiftUI 中使用 Combine 时遇到了麻烦。我习惯于在 AppKit 和 UIKit 中使用键值观察,因为视图控制器不需要相互了解,只需对一些有助于确定状态的全局对象做出反应。

例如,在 AppKit/UIKit 应用程序中,我会创建一个像这样的全局状态对象:

//Global State file
@objc class AppState: NSObject {
  @objc dynamic var project: Project?
}

//Create an instance I can access anywhere in my app
let app = AppState()

然后在视图控制器中,我可以收到有关我的应用范围项目实例的任何更改的通知并做出相应的反应:

//View Controller
class MyViewController: NSViewController{
  var observerProject: NSKeyValueObservation?

  override func viewDidLoad() {
    observerProject = app.observe(\.project) { object, change in
      self.refreshData()
    }
  }
  
  func refreshData(){
    //Query my persistent store and update my UI
  }
}

Combine/SwiftUI 与此类似的是什么?

我需要创建一个发布者然后监听我的全局对象变化吗?如果是这样,我如何让我的核心数据@FetchRequest(其谓词包括我的全局Project 对象)实时响应?

我已经用旧方式做事很长时间了,以至于向 SwiftUI/Combine 的过渡让我很困惑。 ????

【问题讨论】:

    标签: core-data swiftui key-value-observing combine


    【解决方案1】:

    @FetchRequest 不适用于动态谓词(SO 中有一些变通方法),您必须为此使用“老派”NSFetchedResultsController 并将其放入ObservableObject。这是带有示例的video。这是很多设置和代码。

    import SwiftUI
    import Combine
    class AppState: ObservableObject {
        static let shared = AppState()
        @Published var project: String?
        //Just to mimic updates
        var count = 0
        private init() {
            //Mimic getting updates to project
            Timer.scheduledTimer(withTimeInterval: 2, repeats: true){ timer in
                print("Timer")
                self.project = self.count.description
                self.count += 1
                if self.count >= 15{
                    timer.invalidate()
                }
            }
        }
    }
    class MyViewModel: ObservableObject{
        @Published var refreshedData: String = "init"
        let appState: AppState = AppState.shared
        var projectCancellable: AnyCancellable?
        init() {
            
            projectCancellable = appState.$project.sink(receiveValue: {
                value in
                print(value ?? "nil")
                self.refreshData()
            })
        }
            func refreshData() {
            refreshedData = Int.random(in: 0...100).description
        }
    }
    struct MyView: View {
        @StateObject var vm: MyViewModel = MyViewModel()
        var body: some View {
            VStack{
                Text(vm.refreshedData)
            }
        }
    }
    
    struct MyView_Previews: PreviewProvider {
        static var previews: some View {
            MyView()
        }
    }
    

    【讨论】:

    • 这非常有帮助。非常感谢你!我认为我缺少的一个重要部分是制作AppState()static 实例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-08
    • 2015-03-22
    • 1970-01-01
    • 1970-01-01
    • 2021-10-27
    • 2012-02-06
    相关资源
    最近更新 更多