Search code examples
iosswift4scenekitmodelio

.obj file from server URL doesn't work


I need to import 3D model from server URL but it's not working properly. Here is my code:

guard let path = modelPath, !path.isEmpty else {
    fatalError("Failed to find model file path.")
}

guard let modelURL = URL(string: path) else {
    fatalError("Failed to find model URL.")
}

let asset = MDLAsset(url:modelURL)
guard let object = asset.object(at: 0) as? MDLMesh else {
    fatalError("Failed to get mesh from asset.")
}

...crash here at object.


Solution

  • MDLAsset(url:) does not handle downloading models from a server, it's only for URLs that point to local storage.

    You will have to download it yourself (using URLSession or a framework like Alamofire).

    Example using URLSession:

    Download task will return temporary location for the file that will be deleted after the callback closure return so if you need to reuse the file you will have to re-save it somewhere.

    The tempLocation file will have an extension of .tmp, which MDLAsset will not be able to process. Even if you don't need to persist the file, I didn't come up with a better way than to re-save it with the needed extension (.obj that is).

    let fileManager = FileManager.default
    let localModelName = "model.obj"
    let serverModelURL = URL(...)
    let localModelURL = fileManager
            .urls(for: .documentDirectory, in: .userDomainMask[0]
            .appendingPathComponent(localModelName)
    
    let session = URLSession(configuration: .default)
    let task = session.downloadTask(with: modelURL) { tempLocation, response, error in
        guard let tempLocation = tempLocation else {
            // handle error
            return
        }
    
        do {
            // FileManager's copyItem throws an error if the file exist
            // so we check and remove previously downloaded file
            // That's just for testing purposes, you probably wouldn't want to download
            // the same model multiple times instead of just persisting it
    
            if fileManager.fileExists(atPath: localModelURL.path) {
                try fileManager.removeItem(at: localModelURL)
            }
    
            try fileManager.copyItem(at: tempLocation, to: localModelURL)
    
        } catch {
            // handle error
        }
    
        let asset = MDLAsset(url: localURL)
        guard let object = asset.object(at: 0) as? MDLMesh else {
            fatalError("Failed to get mesh from asset.")
        }
    }
    
    task.resume() // don't forget to call resume to start downloading