Model Management
Download models on-demand, track progress, check availability, and manage storage with KuzcoModelManager.
Checking Model Availability
Check if a model is already downloaded before use:
import Kuzcolet manager = KuzcoModelManager.shared// Check single modellet isAvailable = await manager.isModelAvailable(.qwen3_4b)if isAvailable { print("Model ready to use")} else { print("Model needs to be downloaded")}// Check multiple modelslet models: [KuzcoModel] = [.qwen3_4b, .qwen3_8b, .stableDiffusion21]for model in models { let available = await manager.isModelAvailable(model) print("\(model): \(available ? "Ready" : "Not downloaded")")}Downloading Models
Download models with progress tracking:
ModelDownloader.swift
import SwiftUIimport Kuzcostruct ModelDownloadView: View { @State private var progress: Double = 0 @State private var isDownloading = false @State private var downloadComplete = false @State private var errorMessage: String? var body: some View { VStack(spacing: 20) { Text("Qwen3 4B") .font(.headline) if isDownloading { VStack { ProgressView(value: progress) .progressViewStyle(.linear) Text("\(Int(progress * 100))% - Downloading...") .font(.caption) .foregroundColor(.secondary) } } else if downloadComplete { Label("Download Complete", systemImage: "checkmark.circle.fill") .foregroundColor(.green) } else if let error = errorMessage { Text(error) .foregroundColor(.red) } Button(isDownloading ? "Downloading..." : "Download Model") { downloadModel() } .disabled(isDownloading || downloadComplete) } .padding() } func downloadModel() { isDownloading = true errorMessage = nil Task { do { for try await update in KuzcoModelManager.shared.downloadModel(.qwen3_4b) { progress = update.progress if update.isComplete { downloadComplete = true } } } catch { errorMessage = error.localizedDescription } isDownloading = false } }}Download Progress Structure
The download stream provides DownloadProgress updates:
for try await progress in manager.downloadModel(.qwen3_4b) { // Progress from 0.0 to 1.0 print("Progress: \(Int(progress.progress * 100))%") // Bytes downloaded and total print("Downloaded: \(progress.bytesDownloaded) / \(progress.totalBytes)") // Download speed (bytes per second) if let speed = progress.bytesPerSecond { print("Speed: \(speed / 1024 / 1024) MB/s") } // Estimated time remaining if let eta = progress.estimatedTimeRemaining { print("ETA: \(Int(eta)) seconds") } // Check completion if progress.isComplete { print("Download finished!") }}Download All Required Models
Download multiple models for your app's features:
func downloadRequiredModels() async throws { let requiredModels: [KuzcoModel] = [ .qwen3_4b, // For chat .qwen3VL, // For vision .stableDiffusion21 // For image generation ] for model in requiredModels { let available = await KuzcoModelManager.shared.isModelAvailable(model) if !available { print("Downloading \(model)...") for try await progress in KuzcoModelManager.shared.downloadModel(model) { print("\(model): \(Int(progress.progress * 100))%") } print("\(model) downloaded!") } } print("All models ready!")}Cancel Downloads
Cancel ongoing downloads using task cancellation:
class DownloadManager: ObservableObject { @Published var progress: Double = 0 @Published var isDownloading = false private var downloadTask: Task<Void, Never>? func startDownload(model: KuzcoModel) { isDownloading = true downloadTask = Task { do { for try await update in KuzcoModelManager.shared.downloadModel(model) { await MainActor.run { progress = update.progress } } } catch is CancellationError { print("Download cancelled") } catch { print("Error: \(error)") } await MainActor.run { isDownloading = false } } } func cancelDownload() { downloadTask?.cancel() }}Deleting Models
Remove downloaded models to free up storage:
let manager = KuzcoModelManager.shared// Delete a specific modeltry await manager.deleteModel(.qwen3_8b)// Delete multiple modelslet modelsToDelete: [KuzcoModel] = [.qwen3_8b, .stableDiffusion21]for model in modelsToDelete { try await manager.deleteModel(model) print("Deleted \(model)")}// Delete all downloaded modelstry await manager.deleteAllModels()Storage Information
Check storage usage for downloaded models:
let manager = KuzcoModelManager.shared// Get total storage used by all modelslet totalBytes = await manager.totalStorageUsed()let totalGB = Double(totalBytes) / 1024 / 1024 / 1024print("Total storage: \(String(format: "%.2f", totalGB)) GB")// Get storage for a specific modelif let modelSize = await manager.storageUsed(for: .qwen3_4b) { let sizeGB = Double(modelSize) / 1024 / 1024 / 1024 print("Qwen3 4B: \(String(format: "%.2f", sizeGB)) GB")}// List all downloaded models with sizeslet downloadedModels = await manager.downloadedModels()for model in downloadedModels { if let size = await manager.storageUsed(for: model) { print("\(model): \(size / 1024 / 1024) MB") }}Storage Locations
Models are stored in the app's documents directory:
// Default storage locationlet modelsDirectory = KuzcoModelManager.shared.modelsDirectoryprint("Models stored at: \(modelsDirectory.path)")// Get path for a specific modellet modelPath = await KuzcoModelManager.shared.modelPath(for: .qwen3_4b)print("Qwen3 4B path: \(modelPath?.path ?? "Not downloaded")")Storage Considerations
Models can be large (1-5 GB each). Consider implementing storage management UI so users can delete unused models. Models persist across app updates.
Background Downloads
Enable background downloads for large models:
// Enable background downloads (app continues downloading when backgrounded)KuzcoModelManager.shared.enableBackgroundDownloads = true// Handle background completion in your AppDelegate or SceneDelegatefunc handleBackgroundDownloadCompletion(identifier: String) { KuzcoModelManager.shared.handleBackgroundCompletion(identifier: identifier)}Model Download UI Pattern
A complete model management UI pattern:
ModelManager.swift
import SwiftUIimport Kuzcostruct ModelManagerView: View { @StateObject private var viewModel = ModelManagerViewModel() var body: some View { List { ForEach(KuzcoModel.allCases, id: \.self) { model in ModelRow( model: model, status: viewModel.status(for: model), progress: viewModel.progress(for: model), onDownload: { viewModel.download(model) }, onDelete: { viewModel.delete(model) }, onCancel: { viewModel.cancel(model) } ) } } .navigationTitle("Models") .task { await viewModel.checkAvailability() } }}@MainActorclass ModelManagerViewModel: ObservableObject { @Published private var statuses: [KuzcoModel: ModelStatus] = [:] @Published private var progresses: [KuzcoModel: Double] = [:] private var downloadTasks: [KuzcoModel: Task<Void, Never>] = [:] enum ModelStatus { case notDownloaded case downloading case downloaded } func checkAvailability() async { for model in KuzcoModel.allCases { let available = await KuzcoModelManager.shared.isModelAvailable(model) statuses[model] = available ? .downloaded : .notDownloaded } } func download(_ model: KuzcoModel) { statuses[model] = .downloading progresses[model] = 0 downloadTasks[model] = Task { do { for try await progress in KuzcoModelManager.shared.downloadModel(model) { progresses[model] = progress.progress if progress.isComplete { statuses[model] = .downloaded } } } catch { statuses[model] = .notDownloaded } } } func cancel(_ model: KuzcoModel) { downloadTasks[model]?.cancel() statuses[model] = .notDownloaded } func delete(_ model: KuzcoModel) { Task { try? await KuzcoModelManager.shared.deleteModel(model) statuses[model] = .notDownloaded } } func status(for model: KuzcoModel) -> ModelStatus { statuses[model] ?? .notDownloaded } func progress(for model: KuzcoModel) -> Double { progresses[model] ?? 0 }}