I would like to subscribe to UIPasteboard changes in SwiftUI with onReceive.
pHasStringsPublisher will not be updated as soon as something in the clipboard changes and I don't understand why.
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
})
}
}
As far as I know, none of
UIPasteboard's properties are documented to support Key-Value Observing (KVO), sopublisher(for: \.hasStrings)may not ever publish anything.Instead, you can listen for
UIPasteboard.changedNotificationfrom the defaultNotificationCenter. But if you are expecting the user to copy in a string from another application, that is still not sufficient, because a pasteboard doesn't postchangedNotificationif its content was changed while your app was in the background. So you also need to listen forUIApplication.didBecomeActiveNotification.Let's wrap it all up in an extension on
UIPasteboard:And use it like this: