Posts

Post not yet marked as solved
1 Replies
0 Views
The behavior is caused by two separate issues with a SwiftUI Sandboxed app: First, the SCNScene needs to be rendered in order to export correctly. You can't do what I was doing (create a bunch of nodes, stuff them into the scene's root node and call scene.write) and get a correctly rendered usdz. It must first be put on screen in a SwiftUI SceneView, which causes a lot of other initialization to occur. I suppose you could instantiate a SCNRenderer and call prepare() on the root node, but that has some extra complications. Second, the Sandbox prevents a direct export to a URL provided by .fileExporter(). This is because Scene.write() works in two steps: it first creates a .usdc export, and zips the resulting files into a single .usdz. The intermediate files don't have the write privileges the URL provided by .fileExporter() does (assuming you've set the Sandbox "User Selected File" privilege to "Read/Write"), so Scene.write() fails, even if the target URL is writeable, if the target directory is outside the Sandbox. My solution was to write a custom FileWrapper, which I return if the WriteConfiguration UTType is .usdz: public class USDZExportFileWrapper: FileWrapper { var exportScene: SCNScene public init(scene: SCNScene) { exportScene = scene super.init(regularFileWithContents: Data()) } required init?(coder inCoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override public func write(to url: URL, options: FileWrapper.WritingOptions = [], originalContentsURL: URL?) throws { let tempFilePath = NSTemporaryDirectory() + UUID().uuidString + ".usdz" let tempURL = URL(fileURLWithPath: tempFilePath) exportScene.write(to: tempURL, delegate: nil) try FileManager.default.moveItem(at: tempURL, to: url) } } Usage in a ReferenceFileDocument: public func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper { if configuration.contentType == .usdz { return USDZExportFileWrapper(scene: scene) } return .init(regularFileWithContents: snapshot) }