iosswiftnsfilemanager

Supporting Custom Files with "/" Characters in Swift


My app has a custom file type, and I'm trying to update my code so that when I export or import a file that has a "/" character in the name. Currently the app just fails to export a file when it has that character in the name.

This is what I have for exporting the file:

static func exportDocument(_ document: StitchDocument) async -> SentTransferredFile {
        log("StitchDocumentWrapper: transferRepresentation: exporting: called")

        let projectURL = document.getUrl()
        
        // Use the original document name (which may contain '/') for the exported file
        let exportedFileName = document.name + ".stitch"
        
        // Create the temporary export URL in the temporary directory
        let tempExportURL = StitchFileManager.tempDir.appendingPathComponent(exportedFileName)

        log("StitchDocumentWrapper: transferRepresentation: projectURL: \(projectURL)")
        log("StitchDocumentWrapper: transferRepresentation: tempExportURL: \(tempExportURL)")

        do {
            // Remove any existing file at the temp export URL
            try? FileManager.default.removeItem(at: tempExportURL)
            
            // Zip existing project url's contents to the temp export URL
            try FileManager.default.zipItem(at: projectURL, to: tempExportURL)

            // Return the SentTransferredFile with the temp file URL
            return SentTransferredFile(tempExportURL)
        } catch {
            log("StitchDocumentWrapper: transferRepresentation: FAILED: error: \(error)")
            // In case of error, still try to return a file, but it might not contain the correct data
            return SentTransferredFile(tempExportURL)
        }
    }

Note that StitchFileManager is a sub-class of FileManager.

The specific problem is that when the file is exported, it always creates additional directories. So, a file named 1/2/3.stitch would create a file at the following directory:

file:///Users/nicholasarner/Library/Containers/app.stitchdesign.stitch/Data/tmp/1/2/3.stitch

I need to figure out how preserve the file name without creating additional directories due to the inclusion of the / characters.


Solution

  • Forward slashes are simply not allowed in file names.

    The user can give a file a name that contains / in the Files app and other places, because under the hood, the / is getting converted to a :. You can do the same in your app too.

    let exportedFileName = (document.name + ".stitch").replacingOccurrences(of: "/", with: ":")
    

    The Files app and others will replace the : with / when displaying the file names, so it still looks like the file name has a / to the user.

    As a concrete example, here is a view that shares a file named "Foo:Bar.txt".

    struct ContentView: View {
        var body: some View {
            let url = URL.documentsDirectory.appendingPathComponent("Foo:Bar.txt")
            ShareLink(item: url)
            .onAppear {
                FileManager.default.createFile(atPath: url.path(), contents: nil)
            }
        }
    }
    

    After activating the share link, the share sheet shows the file name as "File/Bar":

    enter image description here