import CoreML import Vision import FirebaseStorage struct SharedEngineResources { static let context = CIContext() } // MARK: - MODEL MANAGER (OTA Updates) class ModelManager { static let shared = ModelManager() private let defaults = UserDefaults.standard func getModel(name: String) -> VNCoreMLModel? { // 1. Check Documents (Downloaded Update) if let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { let modelURL = docDir.appendingPathComponent("models/\(name).mlmodelc") if FileManager.default.fileExists(atPath: modelURL.path), let model = try? MLModel(contentsOf: modelURL), let vnModel = try? VNCoreMLModel(for: model) { return vnModel } } // 2. Check Bundle (Built-in Fallback) if let bundleURL = Bundle.main.url(forResource: name, withExtension: "mlmodelc"), let model = try? MLModel(contentsOf: bundleURL), let vnModel = try? VNCoreMLModel(for: model) { return vnModel } return nil } func checkForUpdates() { guard FirebaseApp.app() != nil else { return } let models = ["IYmtgFoilClassifier", "IYmtgConditionClassifier", "IYmtgStampClassifier", "IYmtgSetClassifier"] for name in models { let ref = Storage.storage().reference().child("models/\(name).mlmodel") ref.getMetadata { meta, error in guard let meta = meta, let remoteDate = meta.updated else { return } let localDate = self.defaults.object(forKey: "ModelDate_\(name)") as? Date ?? Date.distantPast if remoteDate > localDate { let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(name).mlmodel") ref.write(toFile: tempURL) { url, error in guard let url = url else { return } DispatchQueue.global(qos: .utility).async { do { let compiledURL = try MLModel.compileModel(at: url) let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("models") try FileManager.default.createDirectory(at: docDir, withIntermediateDirectories: true) let destURL = docDir.appendingPathComponent("\(name).mlmodelc") let tempDestURL = docDir.appendingPathComponent("temp_\(name).mlmodelc") try? FileManager.default.removeItem(at: tempDestURL) try FileManager.default.copyItem(at: compiledURL, to: tempDestURL) if FileManager.default.fileExists(atPath: destURL.path) { _ = try FileManager.default.replaceItem(at: destURL, withItemAt: tempDestURL, backupItemName: nil, options: []) } else { try FileManager.default.moveItem(at: tempDestURL, to: destURL) } self.defaults.set(remoteDate, forKey: "ModelDate_\(name)") print("✅ OTA Model Updated: \(name)") } catch { print("❌ Model Update Failed for \(name): \(error)") } } } } } } } }