Search code examples
swiftuiswiftui-sharelink

SwiftUI ShareLink on optional URL


I want to use ShareLink to save a zipped backup file that needs to be generated in my code. I was able to do this in a clunky way and am looking for something more elegant.

My first way had a button that created the zipped file. When the button code succeeded a second part of the view would show up (as the optional URL variable wasn't nil anymore) that would show the ShareLink view to the user so they could export it. This was clunky and I wasn't sure when I should remove the local zipped file in a clean way and respectful to the user.

My new idea was to replace the ShareLink item (URL) with a function call BUT I don't know how to handle the condition if the URL is nil (zipping the file doesn't work). This works beautifully if I use "!" to force it but if I change my function, for testing, to return nil ONLY just to see what happens the program crashes on app load.

This is my sharelink code on my main view:

ShareLink(item: functionToBackupAndReturnURLifSuccessfulAndNilIfNot()!, label: {
       Label("Backup All Items", systemImage: "square.and.arrow.up.on.square")
})

My simplified function:

private func functionToBackupAndReturnURLifSuccessfulAndNilIfNot() -> URL? {
    let optionalURL = ClassName().exportToFile(dataController: dataController)
    return optionalURL
}

If this function returns only nil then it crashes on program load with Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

private func functionToBackupAndReturnURLifSuccessfulAndNilIfNot() -> URL? {
    return nil
}

Is there anyway to make this work elegantly. I want the sharelink to be a simple button that kicks off the export code but gives an error to the user if the code export fails and shares the share sheet if successful.

Thanks for any help you can provide.


Solution

  • You can't really "show an error" when a user taps on a ShareLink. The best way I can think of is to make a regular button, do the export, show an error with an .alert if it fails, and show a share sheet with the iOS 15 way otherwise.

    The "iOS 15 way" of course wraps a UIActivityViewController. There is currently no way around that. See also this question about showing a SwiftUI share sheet programmatically, which has no answers.

    On the other hand, if you are fine with a sheet showing no share options when the export failed (instead of a more obvious "error" alert), you can create a Transferable value that can only be exported when the URL is not nil, e.g.

    struct Backup: Transferable {
        
        let url: URL?
        
        static var transferRepresentation: some TransferRepresentation {
            FileRepresentation(exportedContentType: .zip) { backup in
                SentTransferredFile(backup.url!)
            }.exportingCondition { $0.url != nil } // this makes the force unwrapping safe
        }
    }
    

    Then you can create your ShareLink like this, with a preview:

    ShareLink(item: Backup(url: functionToBackupAndReturnURLifSuccessfulAndNilIfNot()), preview: .init("Backup"))