I am attempting to make a simple .fileImporter to work for a file where I define my own Uniform Type Identifier. For some reason, when I want to upload a file, I always receive the error
keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))
The interesting thing is that the data is actually available and seems to be correct as I can see when I convert the imported json to a string:
Optional("{"testData":{"name":"name","id":"D4C4C9CC-B7A0-4F79-8803-BF8C720396FF","number":10}}")
Since TestData is defined as Codable, encoding and decoding conformance is already built in. But for some reason the decoding does not seem to work. It also does not work when I write my own codable functions (not included in the following code).
Here is the entire code sample:
import CoreTransferable
import SwiftUI
import UniformTypeIdentifiers
struct ContentView: View {
@State private var showImport = false
var body: some View {
NavigationStack {
ShareLink(item: ExportHandler(testData: TestData(id: UUID().uuidString,name: "name", number: 10)), preview: SharePreview("Share data."))
.toolbar {
Button {
showImport.toggle()
} label: {
Label("Import Course", systemImage: "square.and.arrow.down")
}
}
.fileImporter(isPresented: $showImport, allowedContentTypes: [.srtExportType]) { result in
print("Result: \(result)")
switch result {
case .success(let success):
do {
if success.startAccessingSecurityScopedResource() {
let data = try Data(contentsOf: success)
let str = String(data: data, encoding: .utf8)
print("The data is available:", str)
let decodedParcours = try JSONDecoder().decode(TestData.self, from: data)
}
success.stopAccessingSecurityScopedResource()
} catch {
print("Error opening file: \(error)")
}
case .failure(let failure):
print("Failure to open file: \(failure)")
}
}
}
}
}
struct TestData: Identifiable, Codable {
var id: String
var name: String
var number: Int
init(id: String, name: String, number: Int) {
self.id = id
self.name = name
self.number = number
}
}
struct ExportHandler: Codable, Transferable {
var testData: TestData
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .srtExportType)
.suggestedFileName("File\(Date())")
}
}
extension UTType {
static var srtExportType = UTType(exportedAs: "ch.willowsrun.sharereceivetester")
}
I have also set up the necessary settings for the UTI:
What am I missing?
Note that in the ShareLink
, you are sharing an instance of ExportHandler
:
ShareLink(item: ExportHandler(testData: ...))
However, when you import the data, you try to decode an instance of TestData
:
let decodedParcours = try JSONDecoder().decode(TestData.self, from: data)
This fails, because the JSON represents an instance of ExportHandler
. You can see that it has a "testData"
key.
You should either do
let decodedParcours = try JSONDecoder().decode(ExportHandler.self, from: data).testData
Or make TestData
conform to Transferable
and share that directly. This in my opinion makes more sense. There is no point in having an ExportHandler
if all it does is "to make the data Transferable
". Just conform to Transferable
directly.