SwiftUI onReceive не работает с издателем UIPasteboard

Я хотел бы подписаться на изменения UIPasteboard в SwiftUI с помощью onReceive. pHasStringsPublisher не будет обновляться, как только что-то в буфере обмена изменится, и я не понимаю почему.

import SwiftUI

struct ContentView: View {
    let pasteboard = UIPasteboard.general
    
    @State var pString: String = "pString"
    @State var pHasStrings: Bool = false
    @State var pHasStringsPublisher: Bool = false

    var body: some View {
        VStack{
            Spacer()
            Text("b: '\(self.pString)'")
                .font(.headline)
            Text("b: '\(self.pHasStrings.description)'")
                .font(.headline)
            Text("p: '\(self.pHasStringsPublisher.description)'")
                .font(.headline)
            Spacer()
            Button(action: {
                self.pString = self.pasteboard.string ?? "nil"
                self.pHasStrings = self.pasteboard.hasStrings
            }, label: {
                Text("read pb")
                    .font(.largeTitle)
            })
            Button(action: {
                self.pasteboard.items = []
            }, label: {
                Text("clear pb")
                    .font(.largeTitle)
            })
            Button(action: {
                self.pasteboard.string = Date().description
            }, label: {
                Text("set pb")
                    .font(.largeTitle)
            })
            
        }
        .onReceive(self.pasteboard
                    .publisher(for: \.hasStrings)
                    .print()
                    .receive(on: RunLoop.main)
                    .eraseToAnyPublisher()
                   , perform:
                    { hasStrings in
                        print("pasteboard publisher")
                        self.pHasStringsPublisher = hasStrings
                    })
    }

}

person 3xypn0s    schedule 07.05.2021    source источник
comment
Вы уверены, что hasStrings соответствует требованиям KVO? Метод publisher(for: KeyPath работает только с KVO-совместимыми свойствами, поскольку он использует KVO под капотом. В документации на это свойство не упоминается, что оно совместимо с KVO, поэтому я подозреваю, что на самом деле это не так. Вам нужно найти другой способ наблюдать за изменениями монтажного стола.   -  person Dávid Pásztor    schedule 07.05.2021
comment
Спасибо тебе за пояснение. Я не знал о KVO-совместимых свойствах, но буду иметь это в виду.   -  person 3xypn0s    schedule 08.05.2021


Ответы (1)


Насколько мне известно, ни одно из свойств UIPasteboard не задокументировано для поддержки наблюдения ключевого значения (KVO), поэтому publisher(for: \.hasStrings) может никогда ничего не публиковать.

Вместо этого вы можете прослушивать UIPasteboard.changedNotification по умолчанию NotificationCenter. Но если вы ожидаете, что пользователь скопирует строку из другого приложения, этого все равно недостаточно, потому что монтажный стол не отправляет changedNotification, если его содержимое было изменено, когда ваше приложение находилось в фоновом режиме. Так что вам также нужно прислушиваться к UIApplication.didBecomeActiveNotification.

Давайте заключим все это в расширение на UIPasteboard:

extension UIPasteboard {
    var hasStringsPublisher: AnyPublisher<Bool, Never> {
        return Just(hasStrings)
            .merge(
                with: NotificationCenter.default
                    .publisher(for: UIPasteboard.changedNotification, object: self)
                    .map { _ in self.hasStrings })
            .merge(
                with: NotificationCenter.default
                    .publisher(for: UIApplication.didBecomeActiveNotification, object: nil)
                    .map { _ in self.hasStrings })
            .eraseToAnyPublisher()
    }
}

И используйте это так:

    var body: some View {
        VStack {
            blah blah blah
        }
        .onReceive(UIPasteboard.general.hasStringsPublisher) { hasStrings = $0 }
    }
person rob mayoff    schedule 07.05.2021