Store USDZ with SwiftData

I am trying to store usdz files with SwiftData for now. I am converting usdz to data, then storing it with SwiftData

My model

import Foundation
import SwiftData
import SwiftUI

@Model
class Item {
    var name: String

    @Attribute(.externalStorage)
    var usdz: Data? = nil

    var id: String

    init(name: String, usdz: Data? = nil) {
        self.id = UUID().uuidString
        self.name = name
        self.usdz = usdz
    }
}

My function to convert usdz to data. I am currently a local usdz just to test if it is going to work.

func usdzData() -> Data? {
    do {
        guard let usdzURL = Bundle.main.url(forResource: "tv_retro", withExtension: "usdz") else {
            fatalError("Unable to find USDZ file in the bundle.")
        }
        let usdzData = try Data(contentsOf: usdzURL)
        return usdzData
    } catch {
        print("Error loading USDZ file: \(error)")
    }
    return nil
}

Loading the items

 @Query private var items: [Item]


...
   var body: some View {
     ...
                        ForEach(items) { item in
                            HStack {
                                Model3D(?????) { model in
                                    model
                                        .resizable()
                                        .scaledToFit()

                                } placeholder: {
                                    ProgressView()
                                }
                            }

                        }
     ...
}

How can I load the Model3D?

I have tried: Model3D(data: item.usdz) Gives me the errors: Cannot convert value of type '[Item]' to expected argument type 'Binding<C>' Generic parameter 'C' could not be inferred Both errors are giving in the ForEach.

I am able to print the content inside item:

                        ForEach(items) { item in
                            HStack {
                                Text("\(item.name)")
                                Text("\(item.usdz)")
                            }

                        }

This above works fine for me. The item.usdz prints something like Optional(10954341 bytes)

I would like to know 2 things:

  1. Is this the correct way to save usdz files into SwiftData? Or should I use FileManager? If so, how should I do that?

  2. Also how can I get the usdz from the storage (SwiftData) to my code and use it into Model3D?

I am notice something on the file that is being saved. When I try saving an usdz file like that, a file is created in some tmp foldertmp/351CD7C7-9AAD-43DE-B3C6-7E5C8A1CA70A.tmp The file has tmp at the end. If I change to .usdz I can load the file manually. But I believe we should not be doing that right?

I figure it out how. Basically there are no changes in the Item. The point is the way we open it.

func usdzToData(named: String) -> Data? {
    do {
        createFolderForAppIfNeed()
        guard let usdzURL = Bundle.main.url(forResource: named, withExtension: "usdz") else {
            fatalError("Unable to find USDZ file in the bundle.")
        }
        /// according to https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
         let fileName = UUID().uuidString
        guard let path = getPath()?.appendingPathComponent("\(fileName).usdz") else {
            print("An unexpected error occurred")
            return nil
        }
        print("usdzURL: \(usdzURL)")
        print("path: \(path)")
        
        let usdzData = try Data(contentsOf: usdzURL)
        try usdzData.write(to: path)
        return usdzData
    } catch {
        print("Error loading USDZ file: \(error)")
    }
    return nil
}

func urlFromData(_ data: Data, withExtension fileExtension: String = "usdz") -> URL? {
    do {
        createFolderForAppIfNeed()
        let tempDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
        let fileName = UUID().uuidString
        let tempFileURL = tempDirectory!.appendingPathComponent(folderName).appendingPathComponent(fileName).appendingPathExtension(fileExtension)
        try data.write(to: tempFileURL)
        print(String(describing: tempFileURL))
        return tempFileURL
    } catch {
        print(error)
        return nil
    }
}

Even though this is working. I would like to know if there is a better way. because it seems overkill. Since it says everywhere that SwiftData magically manage your files and so on. It would be cool If I can just do like:

Model3D(from: item.usdz)

Instead of

Model3D(url: urlFromData(item.usdz!)!)

I think that storing small amounts of arbitrary data is stored as a blob in the database and large blocks are saved in the file system with the database having a pointer to the file. I would expect the intended way to retrieve the data is using a fetch request rather than trying to access the file yourself.

Store USDZ with SwiftData
 
 
Q