SwiftUI - 不会触发 Combine 的 KV Observe 完成
SwiftUI - KV Observe completion from Combine does not get triggered
我正在尝试使用名为 VailerSIPLib
的库构建一个 VOIP
应用程序。由于库是使用 Obj-C
构建的,并且大量使用 NotificationCenter
来发布所有地方的活动状态更改。
我目前在项目的 CallView
部分,我可以设法开始、结束、拒绝来电。但是,我需要在视图中实现 connectionStatus
,它将提供有关通话的信息,例如持续时间、"connecting.."、"disconnected"、"ringing" 等
以下代码全部在CallViewModel: ObservableObject
;
变量:
var activeCall: VSLCall!
@Published var connectionStatus: String = ""
初始化程序:
override init(){
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(self.listen(_:)), name: Notification.Name.VSLCallStateChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.buildCallView(_:)), name: Notification.Name.CallKitProviderDelegateInboundCallAccepted, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.buildCallView(_:)), name: Notification.Name.CallKitProviderDelegateOutboundCallStarted, object: nil)
}
方法:
func setCall(_ call: VSLCall) {
self.activeCall = call
self.activeCall.observe(\.callStateText) { (asd, change) in
print("observing")
print("\(String(describing: change.oldValue)) to \(String(describing: change.newValue)) for \(call.callId)")
}
}
@objc func listen(_ notification: Notification) {
if let _ = self.activeCall {
print(self.activeCall.callStateText)
}
}
@objc func buildCallView(_ notification: Notification) {
print("inbound call")
self.isOnCall = true
}
问题:
它打印出 setCall(_:)
中除 completionBlock
之外的所有内容。 listen(_:)
函数验证 activeCall
的状态正在改变,我想直接使用它,但它并不总是正确工作。它应该在用 callState
值 .confirmed
应答呼叫时触发,但有时会触发。这样我就知道是时候启动计时器了。
另一点是,在 VialerSIPLib
的示例项目中,他们使用了 self.activeCall.addObserver(_:)
并且工作正常。问题在于它会在类似 didObservedValueChange(_:)
的方法中抛出运行时错误并记录 An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
最后在 activeCall.observe(_:)
处出现黄色警告说
Result of call to 'observe(_:options:changeHandler:)' is unused
which I could not find anything related to it.
Finally there is yellow warning at the activeCall.observe(_:) says
Result of call to 'observe(_:options:changeHandler:)'
这是在告诉你问题出在哪里。 observe(_:options:changeHandler:)
方法只有incompletely documented。它 returns 一个 NSKeyValueObservation
类型的对象,表示您注册为键值观察者。你需要保存这个对象,因为当 NSKeyValueObservation
被销毁时,它会注销你。所以需要在CallViewModel
中加一个属性来存储:
class CallViewModel: ObservableObject {
private var callStateTextObservation: NSKeyValueObservation?
...
然后你需要存储观察结果:
func setCall(_ call: VSLCall) {
activeCall = call
callStateTextObservation = activeCall.observe(\.callStateText) { _, change in
print("observing")
print("\(String(describing: change.oldValue)) to \(String(describing: change.newValue)) for \(call.callId)")
}
}
您可以选择将 Combine API 用于 KVO,尽管它的文档比 Foundation API 更少。您会得到一个 Publisher
,其输出是观察到的每个新值 属性。它是这样工作的:
class CallViewModel: ObservableObject {
private var callStateTextTicket: AnyCancellable?
...
func setCall(_ call: VSLCall) {
activeCall = call
callStateTextTicket = self.activeCall.publisher(for: \.callStateText, options: [])
.sink { print("callId: \(call.callId), callStateText: \([=13=])") }
}
没有特定理由在您的示例代码中使用 Combine API,但通常 Publisher
比 NSKeyValueObservation
更灵活,因为 Combine 提供了多种操作方式在 Publisher
s.
您的 addObserver(_:forKeyPath:options:context:)
错误是因为 API 的年龄要大得多。它早在 Swift 发明之前就被添加到 NSObject
中。事实上,它是在 Objective-C 甚至有块(闭包)之前添加的。当您使用该方法时,所有通知都会发送到观察者的 observeValue(forKeyPath:of:change:context:)
方法。如果您不实现 observeValue
方法,NSObject
中的默认实现会收到通知并引发异常。
我正在尝试使用名为 VailerSIPLib
的库构建一个 VOIP
应用程序。由于库是使用 Obj-C
构建的,并且大量使用 NotificationCenter
来发布所有地方的活动状态更改。
我目前在项目的 CallView
部分,我可以设法开始、结束、拒绝来电。但是,我需要在视图中实现 connectionStatus
,它将提供有关通话的信息,例如持续时间、"connecting.."、"disconnected"、"ringing" 等
以下代码全部在CallViewModel: ObservableObject
;
变量:
var activeCall: VSLCall!
@Published var connectionStatus: String = ""
初始化程序:
override init(){
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(self.listen(_:)), name: Notification.Name.VSLCallStateChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.buildCallView(_:)), name: Notification.Name.CallKitProviderDelegateInboundCallAccepted, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.buildCallView(_:)), name: Notification.Name.CallKitProviderDelegateOutboundCallStarted, object: nil)
}
方法:
func setCall(_ call: VSLCall) {
self.activeCall = call
self.activeCall.observe(\.callStateText) { (asd, change) in
print("observing")
print("\(String(describing: change.oldValue)) to \(String(describing: change.newValue)) for \(call.callId)")
}
}
@objc func listen(_ notification: Notification) {
if let _ = self.activeCall {
print(self.activeCall.callStateText)
}
}
@objc func buildCallView(_ notification: Notification) {
print("inbound call")
self.isOnCall = true
}
问题:
它打印出 setCall(_:)
中除 completionBlock
之外的所有内容。 listen(_:)
函数验证 activeCall
的状态正在改变,我想直接使用它,但它并不总是正确工作。它应该在用 callState
值 .confirmed
应答呼叫时触发,但有时会触发。这样我就知道是时候启动计时器了。
另一点是,在 VialerSIPLib
的示例项目中,他们使用了 self.activeCall.addObserver(_:)
并且工作正常。问题在于它会在类似 didObservedValueChange(_:)
的方法中抛出运行时错误并记录 An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
最后在 activeCall.observe(_:)
处出现黄色警告说
Result of call to 'observe(_:options:changeHandler:)' is unused
which I could not find anything related to it.
Finally there is yellow warning at the activeCall.observe(_:) says
Result of call to 'observe(_:options:changeHandler:)'
这是在告诉你问题出在哪里。 observe(_:options:changeHandler:)
方法只有incompletely documented。它 returns 一个 NSKeyValueObservation
类型的对象,表示您注册为键值观察者。你需要保存这个对象,因为当 NSKeyValueObservation
被销毁时,它会注销你。所以需要在CallViewModel
中加一个属性来存储:
class CallViewModel: ObservableObject {
private var callStateTextObservation: NSKeyValueObservation?
...
然后你需要存储观察结果:
func setCall(_ call: VSLCall) {
activeCall = call
callStateTextObservation = activeCall.observe(\.callStateText) { _, change in
print("observing")
print("\(String(describing: change.oldValue)) to \(String(describing: change.newValue)) for \(call.callId)")
}
}
您可以选择将 Combine API 用于 KVO,尽管它的文档比 Foundation API 更少。您会得到一个 Publisher
,其输出是观察到的每个新值 属性。它是这样工作的:
class CallViewModel: ObservableObject {
private var callStateTextTicket: AnyCancellable?
...
func setCall(_ call: VSLCall) {
activeCall = call
callStateTextTicket = self.activeCall.publisher(for: \.callStateText, options: [])
.sink { print("callId: \(call.callId), callStateText: \([=13=])") }
}
没有特定理由在您的示例代码中使用 Combine API,但通常 Publisher
比 NSKeyValueObservation
更灵活,因为 Combine 提供了多种操作方式在 Publisher
s.
您的 addObserver(_:forKeyPath:options:context:)
错误是因为 API 的年龄要大得多。它早在 Swift 发明之前就被添加到 NSObject
中。事实上,它是在 Objective-C 甚至有块(闭包)之前添加的。当您使用该方法时,所有通知都会发送到观察者的 observeValue(forKeyPath:of:change:context:)
方法。如果您不实现 observeValue
方法,NSObject
中的默认实现会收到通知并引发异常。