Search code examples
swiftalamofirecoremlalamofireobjectmapper

How to save a downloaded CoreML Model with CoreData?


I have downloaded the new trained model from python backend, with this code:

    import Foundation
    import Alamofire
    import AlamofireObjectMapper
    import ObjectMapper
    import CoreML

    class func downloadModel(modelUrl: String) {

        let destenation = Support.getDestenationForModel(modelUrl: modelUrl)

        AF.download(modelUrl, to: destenation).downloadProgress { progress in
            print("Download Progress: \(progress.fractionCompleted)")
        }
            .response { response in
                switch response.result {
                case .success(let success):
                    if let url = success?.absoluteURL {
                        FTApi.compileNewModel(url: url)
                    }
                    break
                case .failure(let err):
                    print(err)
                    break
                }
        }
    }

where in Support class the getDestenationForModel is the following:

    class func getDestenationForModel(modelUrl: String) -> DownloadRequest.Destination {
        let destenation: DownloadRequest.Destination =  { url, options in
            guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first ,
                let modelUrl = URL(string: modelUrl) else {
                    preconditionFailure("unable to use documents directory")
            }

            let fileURL = documentsURL.appendingPathComponent(modelUrl.lastPathComponent)

            AppPrefrences.pathOfModel = modelUrl.lastPathComponent
            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }
        return destenation
    }

when the download is finished I try to save the model to CoreData (I didn't find any other way, maybe you could recommend something else) using the following code:

    private class func compileNewModel(url: URL) {
        do {
            let compiledUrl = try MLModel.compileModel(at: url)
            guard let model = try? MLModel(contentsOf: compiledUrl)
                else {
                    print("cannot get content of model")
                    return
            }

            let coreMlModel = CML(context: PersistanceService.context)
            print(type(of: model)) // => MLNeuralNetworkEngine
            coreMlModel.model = (model as NSObject)
            PersistanceService.saveContext() // here I have an error
        } catch {
            print("cannot download the model!")
        }
    }

the error is: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MLNeuralNetworkEngine encodeWithCoder:]: unrecognized selector sent to instance 0x104d38280'

where this is my CoreData entity class:

import Foundation
import CoreData


extension CML {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<CML> {
        return NSFetchRequest<CML>(entityName: "CML")
    }

    @NSManaged public var model: NSObject?

}

I tried to change the type from NSObject to MLModel, but the result is the same.


Solution

  • Why would you even try this? It's not the sort of thing CoreData is designed for.

    The correct solution is to copy the folder from compiledURL to a location in your app's Application Support folder. Then when you create the MLModel object, use the URL from the Application Support folder.

    Read "Downloading and Compiling a Model on the User's Device" in the Core ML documentation to see how to do this.