Reputation: 862
I have an observable class, which has an observer on UIPasteboard. In an attempt to update to the Swift 6 language mode, I understandably can't mutate the state from the completion handler.
@Observable
final class ImageClipboard {
var images: [SelectableImage]
init() {
self.images = []
NotificationCenter.default.addObserver(forName: UIPasteboard.changedNotification, object: nil, queue: .main) { _ in
if let pasteboardImages = UIPasteboard.general.images {
let newImages = pasteboardImages.map {
SelectableImage(image: $0)
}
self.images.append(contentsOf: newImages)
}
}
}
}
My preference would be to use an asynchronous mechanism to mutate images, otherwise some synchronization mechanism. What would be the best way to deal with this issue?
Upvotes: 1
Views: 64
Reputation: 274835
I think getting the notifications as a Combine publisher (publisher(for:object:)
) is more convenient in this case.
@Observable
@MainActor
class ImageClipboard {
var images: [SelectableImage] = []
@ObservationIgnored
var cancellable: AnyCancellable?
init() {
cancellable = NotificationCenter.default.publisher(for: UIPasteboard.changedNotification)
.receive(on: DispatchQueue.main)
.sink { _ in
let images = UIPasteboard.general.images?.map { SelectableImage(image: $0) } ?? []
self.images.append(contentsOf: images)
}
}
}
The publisher is cancelled automatically when ImageClipboard
is deinitialised.
There is also notifications(named:object:)
, which you can use like this:
@Observable
@MainActor
class ImageClipboard {
var images: [SelectableImage] = []
func startMonitoringClipboard() async {
for await _ in NotificationCenter.default.notifications(named: UIPasteboard.changedNotification) {
let images = UIPasteboard.general.images?.map { SelectableImage(image: $0) } ?? []
self.images.append(contentsOf: images)
}
}
}
Then in your view you should call startMonitoringClipboard
in a .task
:
SomeView()
.task {
await clipboard.startMonitoringClipboard()
}
The task will be cancelled when SomeView
disappears.
Upvotes: 1