Integração Core ML Swift
Carregue, configure e execute modelos Core ML em aplicativos iOS. Esta skill cobre o lado Swift: carregamento de modelos, previsão, MLTensor, perfil de desempenho e implantação. Destina-se ao iOS 26+ com Swift 6.3, com suporte retroativo ao iOS 14, a menos que indicado.
Limite de escopo: A conversão de modelos do lado Python, otimização (quantização, paletização, poda) e seleção de framework estão na skill
apple-on-device-ai. Esta skill possui apenas a integração Swift.
Consulte references/coreml-swift-integration.md para padrões de código completos, incluindo cache baseado em atores, inferência em lote, pré-processamento de imagem e testes.
Conteúdo
- Carregando Modelos
- Configuração do Modelo
- Fazendo Previsões
- MLTensor (iOS 18+)
- Trabalhando com MLMultiArray
- Pré-processamento de Imagem
- Pipelines Multi-Modelo
- Integração com Vision
- Perfil de Desempenho
- Implantação do Modelo
- Gerenciamento de Memória
- Erros Comuns
- Lista de Verificação de Revisão
- Referências
Carregando Modelos
Classes Geradas Automaticamente
Ao arrastar um .mlpackage ou .mlmodelc para o Xcode, ele gera uma classe
Swift com entrada/saída tipada. Use isso sempre que possível.
import CoreML
let config = MLModelConfiguration()
config.computeUnits = .all
let model = try MyImageClassifier(configuration: config)
Carregamento Manual
Carregue a partir de uma URL quando o modelo for baixado em tempo de execução ou armazenado fora do pacote.
let modelURL = Bundle.main.url(
forResource: "MyModel", withExtension: "mlmodelc"
)!
let model = try MLModel(contentsOf: modelURL, configuration: config)
Carregamento Assíncrono (iOS 16+)
Carregue modelos sem bloquear a thread principal. Prefira isso para modelos grandes.
let model = try await MLModel.load(
contentsOf: modelURL,
configuration: config
)
Compilar em Tempo de Execução
Compile um .mlpackage ou .mlmodel para .mlmodelc no dispositivo. Útil para
modelos baixados de um servidor.
let compiledURL = try await MLModel.compileModel(at: packageURL)
let model = try MLModel(contentsOf: compiledURL, configuration: config)
Armazene em cache a URL compilada — recompilar em cada inicialização desperdiça tempo.
Copie compiledURL para um local persistente (por exemplo, Application Support).
Configuração do Modelo
MLModelConfiguration controla unidades de computação, acesso à GPU e parâmetros do modelo.
Tabela de Decisão de Unidades de Computação
| Valor | Usa | Quando Escolher |
|---|---|---|
.all | CPU + GPU + Neural Engine | Padrão. Deixe o sistema decidir. |
.cpuOnly | CPU | Tarefas em segundo plano, sessões de áudio ou quando a GPU estiver ocupada. |
.cpuAndGPU | CPU + GPU | Precisa de GPU, mas o modelo tem operações não suportadas pelo ANE. |
.cpuAndNeuralEngine | CPU + Neural Engine | Melhor eficiência energética para modelos compatíveis. |
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// Permitir inferência em segundo plano de baixa prioridade
config.computeUnits = .cpuOnly
Propriedades de Configuração
let config = MLModelConfiguration()
config.computeUnits = .all
config.allowLowPrecisionAccumulationOnGPU = true // mais rápido, ligeira perda de precisão
Fazendo Previsões
Com Classes Geradas Automaticamente
A classe gerada fornece structs de entrada/saída tipadas.
let model = try MyImageClassifier(configuration: config)
let input = MyImageClassifierInput(image: pixelBuffer)
let output = try model.prediction(input: input)
print(output.classLabel) // "golden_retriever"
print(output.classLabelProbs) // ["golden_retriever": 0.95, ...]
Com MLDictionaryFeatureProvider
Use quando as entradas são dinâmicas ou não conhecidas em tempo de compilação.
let inputFeatures = try MLDictionaryFeatureProvider(dictionary: [
"image": MLFeatureValue(pixelBuffer: pixelBuffer),
"confidence_threshold": MLFeatureValue(double: 0.5),
])
let output = try model.prediction(from: inputFeatures)
let label = output.featureValue(for: "classLabel")?.stringValue
Previsão Assíncrona (iOS 17+)
let output = try await model.prediction(from: inputFeatures)
Previsão em Lote
Processe várias entradas em uma única chamada para melhor rendimento.
let batchInputs = try MLArrayBatchProvider(array: inputs.map { input in
try MLDictionaryFeatureProvider(dictionary: ["image": MLFeatureValue(pixelBuffer: input)])
})
let batchOutput = try model.predictions(from: batchInputs)
for i in 0..<batchOutput.count {
let result = batchOutput.features(at: i)
print(result.featureValue(for: "classLabel")?.stringValue ?? "unknown")
}
Previsão com Estado (iOS 18+)
Use MLState para modelos que mantêm estado entre previsões (modelos de sequência,
LLMs, acumuladores de áudio). Crie o estado uma vez e passe-o para cada chamada de previsão.
let state = model.makeState()
// Cada previsão propaga o estado interno do modelo
for frame in audioFrames {
let input = try MLDictionaryFeatureProvider(dictionary: [
"audio_features": MLFeatureValue(multiArray: frame)
])
let output = try await model.prediction(from: input, using: state)
let classification = output.featureValue(for: "label")?.stringValue
}
O estado não é Sendable — use-o a partir de um único ator ou tarefa. Chame
model.makeState() para criar estados independentes para streams concorrentes.
MLTensor (iOS 18+)
MLTensor é um array multidimensional nativo do Swift para pré/pós-processamento.
As operações são executadas preguiçosamente — chame .shapedArray(of:) para materializar os resultados.
import CoreML
// Criação
let tensor = MLTensor([1.0, 2.0, 3.0, 4.0])
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
// Redimensionamento
let reshaped = tensor.reshaped(to: [2, 2])
// Operações matemáticas
let softmaxed = tensor.softmax()
let normalized = (tensor - tensor.mean()) / tensor.standardDeviation()
// Interoperabilidade com MLMultiArray
let multiArray = try MLMultiArray([1.0, 2.0, 3.0, 4.0])
let fromMultiArray = MLTensor(multiArray)
let backToArray = tensor.shapedArray(of: Float.self)
Trabalhando com MLMultiArray
MLMultiArray é o principal tipo de troca de dados para entradas e saídas de modelos
que não são de imagem. Use-o quando a classe gerada automaticamente esperar características de tipo array.
// Criar um array 3D: [lote, sequência, características]
let array = try MLMultiArray(shape: [1, 128, 768], dataType: .float32)
// Escrever valores
for i in 0..<128 {
array[[0, i, 0] as [NSNumber]] = NSNumber(value: Float(i))
}
// Ler valores
let value = array[[0, 0, 0] as [NSNumber]].floatValue
// Criar a partir de ponteiro de dados para interoperabilidade sem cópia
let data: [Float] = [1.0, 2.0, 3.0]
let fromData = try MLMultiArray(dataPointer: UnsafeMutableRawPointer(mutating: data),
shape: [3],
dataType: .float32,
strides: [1])
Consulte references/coreml-swift-integration.md para padrões avançados de MLMultiArray, incluindo tokenização de NLP e extração de características de áudio.
Pré-processamento de Imagem
Modelos de imagem esperam entrada CVPixelBuffer. Use a conversão CGImage para fotos
da câmera ou biblioteca de fotos. O VNCoreMLRequest do Vision lida com isso
automaticamente; a conversão manual é necessária apenas para previsão direta com MLModel.
import CoreVideo
func createPixelBuffer(from cgImage: CGImage, width: Int, height: Int) -> CVPixelBuffer? {
var pixelBuffer: CVPixelBuffer?
let attrs: [CFString: Any] = [
kCVPixelBufferCGImageCompatibilityKey: true,
kCVPixelBufferCGBitmapContextCompatibilityKey: true,
]
CVPixelBufferCreate(kCFAllocatorDefault, width, height,
kCVPixelFormatType_32ARGB, attrs as CFDictionary, &pixelBuffer)
guard let buffer = pixelBuffer else { return nil }
CVPixelBufferLockBaseAddress(buffer, [])
let context = CGContext(
data: CVPixelBufferGetBaseAddress(buffer),
width: width, height: height,
bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
)
context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
CVPixelBufferUnlockBaseAddress(buffer, [])
return buffer
}
Para padrões adicionais de pré-processamento (normalização, corte central), consulte references/coreml-swift-integration.md.
Pipelines Multi-Modelo
Encadeie modelos quando o pré-processamento ou pós-processamento exigir um modelo separado.
// Inferência sequencial: pré-processador -> modelo principal -> pós-processador
let preprocessed = try preprocessor.prediction(from: rawInput)
let mainOutput = try mainModel.prediction(from: preprocessed)
let finalOutput = try postprocessor.prediction(from: mainOutput)
Para pipelines gerenciados pelo Xcode, use o tipo de modelo de pipeline no .mlpackage.
Cada sub-modelo é executado em sua unidade de computação ideal.
Integração com Vision
Use o Vision para executar modelos de imagem Core ML com pré-processamento automático de imagem (redimensionamento, normalização, espaço de cores, orientação).
Moderno: CoreMLRequest (iOS 18+)
import Vision
import CoreML
let model = try MLModel(contentsOf: modelURL, configuration: config)
let request = CoreMLRequest(model: .init(model))
let results = try await request.perform(on: cgImage)
if let classification = results.first as? ClassificationObservation {
print("\(classification.identifier): \(classification.confidence)")
}
Legado: VNCoreMLRequest
let vnModel = try VNCoreMLModel(for: model)
let request = VNCoreMLRequest(model: vnModel) { request, error in
guard let results = request.results as? [VNRecognizedObjectObservation] else { return }
for observation in results {
let label = observation.labels.first?.identifier ?? "unknown"
let confidence = observation.labels.first?.confidence ?? 0
let boundingBox = observation.boundingBox // coordenadas normalizadas
print("\(label): \(confidence) em \(boundingBox)")
}
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try handler.perform([request])
Para padrões completos do framework Vision (reconhecimento de texto, detecção de código de barras, digitalização de documentos), consulte a skill
vision-framework.
Perfil de Desempenho
MLComputePlan (iOS 17.4+)
Inspecione qual dispositivo de computação cada operação usará antes de executar previsões.
let computePlan = try await MLComputePlan.load(
contentsOf: modelURL, configuration: config
)
guard case let .program(program) = computePlan.modelStructure else { return }
guard let mainFunction = program.functions["main"] else { return }
for operation in mainFunction.block.operations {
let deviceUsage = computePlan.deviceUsage(for: operation)
let estimatedCost = computePlan.estimatedCost(of: operation)
print("\(operation.operatorName): \(deviceUsage?.preferredComputeDevice ?? "desconhecido")")
}
Instruments
Use o template do instrumento Core ML no Instruments para criar perfil:
- Tempo de carregamento do modelo
- Latência de previsão (detalhamento por operação)
- Distribuição do dispositivo de computação (CPU/GPU/ANE por operação)
- Alocação de memória
Execute fora do depurador para resultados precisos (Xcode: Product > Profile).
Implantação do Modelo
Pacote vs. Recursos Sob Demanda
| Estratégia | Prós | Contras |
|---|---|---|
| Empacotar no aplicativo | Disponibilidade instantânea, funciona offline | Aumenta o tamanho do download do aplicativo |
| Recursos sob demanda | Download inicial menor | Requer download antes do primeiro uso |
| Background Assets (iOS 16+) | Baixa antes do tempo | Configuração mais complexa |
| CloudKit / servidor | Flexibilidade máxima | Requer rede, configuração mais demorada |
Considerações de Tamanho
- Limite da App Store: 4 GB para o pacote do aplicativo
- Limite de download por celular: 200 MB (pode solicitar exceção)
- Use tags ODR para modelos > 50 MB
- Pré-compile para
.mlmodelcpara pular a compilação no dispositivo
// Carregamento de recurso sob demanda
let request = NSBundleResourceRequest(tags: ["ml-model-v2"])
try await request.beginAccessingResources()
let modelURL = Bundle.main.url(forResource: "LargeModel", withExtension: "mlmodelc")!
let model = try await MLModel.load(contentsOf: modelURL, configuration: config)
// Chame request.endAccessingResources() quando terminar
Gerenciamento de Memória
- Descarregar em segundo plano: Libere referências ao modelo quando o aplicativo entrar em segundo plano para liberar memória da GPU/ANE. Recarregue ao retornar ao primeiro plano.
- Use
.cpuOnlypara tarefas em segundo plano: O processamento em segundo plano não pode usar GPU ou ANE; definir.cpuOnlyevita fallback silencioso e contenção de recursos. - Compartilhe instâncias do modelo: Nunca crie várias instâncias
MLModeldo mesmo modelo compilado. Use um ator para fornecer acesso compartilhado. - Monitore a pressão de memória: Modelos grandes (>100 MB) podem disparar avisos de memória.
Registre-se para
UIApplication.didReceiveMemoryWarningNotificatione libere modelos em cache quando sob pressão.
Consulte references/coreml-swift-integration.md para um gerenciador de modelos baseado em atores com carregamento ciente do ciclo de vida e remoção de cache.
Erros Comuns
NÃO: Carregue modelos na thread principal.
FAÇA: Use a API assíncrona MLModel.load(contentsOf:configuration:) ou carregue em um ator em segundo plano.
Por quê: Modelos grandes podem levar segundos para carregar, congelando a interface do usuário.
NÃO: Recompile .mlpackage para .mlmodelc em cada inicialização do aplicativo.
FAÇA: Compile uma vez com MLModel.compileModel(at:) e armazene em cache a URL compilada persistentemente.
Por quê: A compilação é cara. Armazene em cache o .mlmodelc no Application Support.
NÃO: Defina .cpuOnly como fixo, a menos que tenha um motivo específico.
FAÇA: Use .all e deixe o sistema escolher a unidade de computação ideal.
… [truncado para tradução]