ShareLink in SwiftUI doesn't offer Copy link

157 views Asked by At

Using any form of ShareLink with an URL or a String having an URL as text, never is the option Copy-Link offered, even when enabled and showed in the settings

ShareLink(item: url)

enter image description here

enter image description here

1

There are 1 answers

0
Peter Lapisu On

i had to resort to NSViewRepresentable and my custom service...

to create a replacable ShareLink <-> XShareLink

XShareLink(item: url) {
   Image(systemName: "ellipsis")
   .imageScale(.large)
}

if anybody finds a more pure SwiftUI way, pls feel free

////////////////////////////////////////////////////////////////////////
// MARK: - Share Link alternative -
////////////////////////////////////////////////////////////////////////

// as the default ShareLink doesn't offer copy link
// https://stackoverflow.com/questions/77220687/sharelink-in-swiftui-doesnt-offer-copy-link

struct XSharingsPicker: 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: XSharingsPicker

        init(owner: XSharingsPicker) {
            self.owner = owner
        }

        func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] {
            
            guard let url = items.compactMap({ $0 as? URL }).first else {
                return proposedServices
            }
            
            guard let image = NSImage(systemSymbolName: "link", accessibilityDescription: nil) else {
                return proposedServices
            }
            
            var share = proposedServices
            let customService = NSSharingService(title: "Copy Link", image: image, alternateImage: image, handler: {
                let p = NSPasteboard.general
                p.clearContents()
                p.setString(url.absoluteString, forType: .string)
            })
            share.insert(customService, at: 0)
            
            return share
        }
        
        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
        }
    }
}

struct XShareLink<Label>: View where Label: View {
    
    var item: URL
    var label: () -> Label
    
    @State private var showPicker = false
    
    var body: some View {
        Button(action: {
            self.showPicker = true
        }) {
            label()
        }
        .background(XSharingsPicker(isPresented: $showPicker, sharingItems: [item]))
    }
}