Search code examples
swiftui

How to implement lazy ShareLink in swiftUI


I'm curious, does anyone know how we can implement a lazy version of ShareLink for SwiftUI?

Imagine if the item you're sharing involves some significant processing. For instance, you're generating a report that requires processing of lots of data. I wouldn't want that processing to occur just to create a button to generate the report; instead, I'd want to supply a closure that would be called to generate the report only when the user taps on the button.

Running this example code confirms to me that the normal API isn't doing what I'd like (when the app launches, I see the message "generateReport has been called", even before I tap the ShareLink):

struct ShareTest: View {
    func generateReport() -> String {
        print ("generateReport has been called")
        return "This is an expensive report"
    }
    
    var body: some View {
        ShareLink(item: generateReport())
    }
}

Is there some API I've missed that lets me achieve lazy computing of the report? If there is no API that supports it directly, is there a creative way of accomplishing this?


Solution

  • If the generated report is itself Transferable (e.g. String), you can make your own Transferable type with a ProxyRepresentation:

    struct Report: Transferable {
        
        // you can add properties here that can be used for generating the report
    
        func generateReport() -> String {
            print("Generating...")
            // do some work...
            return "A generated report"
        }
        
        static var transferRepresentation: some TransferRepresentation {
            ProxyRepresentation { report in
                report.generateReport()
            }
        }
    }
    
    ShareLink(
        item: Report(/* pass information needed here*/), 
        preview: .init("Some Report")
    )
    

    "Generating..." will only be printed when the user chooses a share action.

    If generateReport writes the report to a file, and returns a URL to that file, you should use a FileRepresentation. (See also this post)

    If the report is not Transferable, you can convert it into Data, and use a DataRepresentation instead.