swiftswiftuiswiftui-sharelink

ShareLink in SwiftUI doesn't offer Copy link


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


Solution

  • 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]))
        }
    }