Search code examples
swiftmacosswiftui

SwiftUI using NSSharingServicePicker in MacOS


I am trying to use a Share function inside my MacOS app in SwiftUI. I am having a URL to a file, which I want to share. It can be images/ documents and much more.

I found NSSharingServicePicker for MacOS and would like to use it. However, I am struggeling to use it in SwiftUI.

Following the documentation, I am creating it like this:

let shareItems = [...]

let sharingPicker : NSSharingServicePicker = NSSharingServicePicker.init(items: shareItems as [Any])

sharingPicker.show(relativeTo: NSZeroRect, of:shareView, preferredEdge: .minY)

My problem is in that show() method. I need to set a NSRect, where I can use NSZeroRect.. but I am struggeling with of: parameter. It requires a NSView. How can I convert my current view as NSView and use it that way. Or can I use my Button as NSView(). I am struggling with that approach.

Another option would be to use a NSViewRepresentable. But should I just create a NSView and use it for that method.


Solution

  • Here is minimal working demo example

    demo

    struct SharingsPicker: NSViewRepresentable {
        @Binding var isPresented: Bool
        var sharingItems: [Any] = []
    
        func makeNSView(context: Context) -> NSView {
            let view = NSView()
            return view
        }
    
        func updateNSView(_ nsView: NSView, context: Context) {
            if isPresented {
                let picker = NSSharingServicePicker(items: sharingItems)
                picker.delegate = context.coordinator
    
                // !! MUST BE CALLED IN ASYNC, otherwise blocks update
                DispatchQueue.main.async {
                    picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY)
                }
            }
        }
    
        func makeCoordinator() -> Coordinator {
            Coordinator(owner: self)
        }
    
        class Coordinator: NSObject, NSSharingServicePickerDelegate {
            let owner: SharingsPicker
    
            init(owner: SharingsPicker) {
                self.owner = owner
            }
    
            func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose service: NSSharingService?) {
    
                // do here whatever more needed here with selected service
    
                sharingServicePicker.delegate = nil   // << cleanup
                self.owner.isPresented = false        // << dismiss
            }
        }
    }
    

    Demo of usage:

    struct TestSharingService: View {
        @State private var showPicker = false
        var body: some View {
            Button("Share") {
                self.showPicker = true
            }
            .background(SharingsPicker(isPresented: $showPicker, sharingItems: ["Message"]))
        }
    }