SwiftUI - KV Наблюдение за завершением из комбайна не срабатывает - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь создать приложение VOIP, используя lib под названием VailerSIPLib. Поскольку библиотека была построена с использованием Obj-C и интенсивно с использованием NotificationCenter, чтобы опубликовать sh, изменения активных состояний повсюду.

Я в настоящее время в CallView части проекта, я могу начать, завершить, отклонить вызовы. Тем не менее, мне нужно реализовать connectionStatus в представлении, которое будет предоставлять информацию о вызове, например, длительность, «подключение ..», «отключение», «звонок» и т. Д. c.

Все приведенные ниже коды приведены в 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 
}

Проблема:

Он распечатывает все, кроме completionBlock в setCall(_:). Функция 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 который я не смог найти ничего связанного с этим.

1 Ответ

1 голос
/ 06 марта 2020

Наконец, в activeCall.observe есть жёлтое предупреждение (_ :) говорит

Result of call to 'observe(_:options:changeHandler:)'

Это говорит вам, в чем проблема. observe(_:options:changeHandler:) метод только не полностью задокументирован . Он возвращает объект типа 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: \($0)") }
    }

Нет никакой определенной c причины для использования Combine API в вашем примере кода, но в целом Publisher является более гибким, чем NSKeyValueObservation, потому что Combine обеспечивает множество способов работы с Publisher s.


Ваша ошибка с addObserver(_:forKeyPath:options:context:) происходит из-за того, что это намного более старый API. Он был добавлен в NSObject задолго до того, как был изобретен Свифт. Фактически, он был добавлен до того, как у Objective- C даже были блоки (замыкания). При использовании этого метода все уведомления отправляются методу observeValue(forKeyPath:of:change:context:) наблюдателя. Если вы не реализуете метод observeValue, реализация по умолчанию в NSObject получает уведомление и вызывает исключение.

...