cd /news/machine-learning/wiring-mlx-to-swift-running-fine-tun… Β· home β€Ί topics β€Ί machine-learning β€Ί article
[ARTICLE Β· art-103002] src=dev.to β†— pub= topic=machine-learning verified=true sentiment=↑ positive

Wiring MLX to Swift: Running Fine-Tuned Models on Apple Silicon with Zero CoreML Overhead

A developer demonstrates how to run fine-tuned large language models on Apple Silicon using MLX Swift bindings, bypassing CoreML's compilation latency. The approach uses lazy evaluation, direct loading of .safetensors weights, and Swift 6 actors for KV-cache management, with benchmarks showing MLX outperforming CoreML on M-series chips.

read5 min views3 publishedAug 19, 2026
---
title: "Wiring MLX to Swift: Running Fine-Tuned Models on Apple Silicon with Zero CoreML Overhead"
published: true
description: "MLX Swift bindings bypass CoreML's compilation latency. Load quantized fine-tuned LLMs via swift-transformers, manage KV-cache with Swift 6 actors, and see where MLX beats CoreML on M-series chips."
tags: swift, mobile, architecture, ios
canonical_url: https://mvpfactory.co/blog/mlx-swift-fine-tuned-llms-apple-silicon
---

## What You Will Build

By the end of this tutorial, you will have a working Swift setup that loads a fine-tuned LLM directly via MLX Swift bindings β€” no CoreML compilation step, no ANE scheduling overhead, no app bundle bloat from pre-compiled `.mlmodelc` packages.

Let me show you a pattern I use in every project that ships on-device language models on Apple Silicon.

## Prerequisites

- macOS on Apple Silicon (M1, M2, or M3) β€” MLX targets macOS as its primary environment
- Xcode 15+ with Swift 6 concurrency enabled
- Python environment for weight conversion (mlx-lm toolchain)
- A fine-tuned model checkpoint exported to `.safetensors`

## Why Not Just Use CoreML?

CoreML is the default assumption for on-device inference, and for vision and classification tasks it earns that position. For autoregressive LLM generation β€” especially models you are iterating on β€” it compounds friction fast.

First-load compilation for a 3B parameter model takes 15–45 seconds on M2. Every fine-tune iteration requires a new compilation cycle. Your app bundle grows with each compiled artifact. For teams shipping model updates from a server, this is a real cost.

MLX sidesteps this entirely. It operates on a lazy computation graph backed by Metal, fusing kernels across the unified memory architecture M-series chips use.

## Step 1: Understand MLX's Lazy Evaluation Model

Operations on `MLXArray` are not executed until you call `MLX.eval()`. This is different from CoreML's eager model, where each layer boundary is a potential synchronization point.

swift

import MLX

import MLXRandom

// Lazy β€” no compute happens here

let weights = MLXArray(converting: loadedFloatArray)

let input = MLXArray(tokenIds, dtype: .int32)

// Compute graph is built, then evaluated in one fused pass

let logits = model(input, weights)

MLX.eval(logits)

## Step 2: Load Your Fine-Tuned Weights

Convert your checkpoint to `.safetensors` once using the mlx-lm Python toolchain. Then load directly β€” no per-device compilation, no format migration between chip generations.

swift

import Transformers

import MLX

// Tokenizer loaded via swift-transformers

let tokenizer = try await AutoTokenizer.from(pretrained: "your-org/your-finetuned-model")

// Weights loaded from locally converted .safetensors checkpoint

let weightsURL = localCheckpointURL.appendingPathComponent("weights.safetensors")

let arrays = try MLX.loadArrays(url: weightsURL) // see mlx-swift docs for current API

The same checkpoint loads on M1, M2, and M3 without changes. That portability compounds over time when you are shipping model updates from a server.

## Step 3: Wrap Your KV-Cache in a Swift 6 Actor

Autoregressive generation needs KV-cache management to avoid redundant computation. Here is the minimal setup to get this working safely with Swift 6 concurrency:

swift

actor KVCache {

private var keys: [MLXArray] = []

private var values: [MLXArray] = []

func append(key: MLXArray, value: MLXArray) {
    keys.append(key)
    values.append(value)
}

func concatenated() -> (MLXArray, MLXArray) {
    return (MLX.concatenated(keys, axis: 1),
            MLX.concatenated(values, axis: 1))
}

}

The actor boundary gives you `Sendable` conformance and data-race safety for free. The isolation cost is negligible against generation latency.

## Benchmark: MLX vs CoreML on M-Series

These figures are representative of community benchmarks on M2 Pro (macOS 14.x), Mistral-7B-Instruct at 4-bit quantization, single-sequence generation. Treat them as directional β€” your results will vary by chip SKU and thermal state.

| Metric | CoreML (ANE) | MLX (Metal GPU) |
|---|---|---|
| First-load compile (3B model) | 15–45s | 0s |
| Time-to-first-token (4-bit quant) | ~120ms | ~60–80ms |
| Tokens/sec (7B 4-bit, sustained) | ~25 tok/s | ~40–55 tok/s |
| Fine-tune iteration cycle | Recompile required | Reload weights only |

## Gotchas

**iOS memory budget will stop you cold.** A 7B model at 4-bit quantization needs approximately 4GB of RAM. The iPhone 15 Pro tops out at 8GB β€” leaving almost no headroom alongside the OS and app stack. MLX Swift is the right call for macOS apps and developer tooling. For iPhone, look at sub-2B models with CoreML or llama.cpp via C interop.

**Thermal throttling on iPhone is aggressive.** MLX runs GPU-primary, trading ANE efficiency for flexibility. On sustained generation workloads, battery-sensitive iOS apps will feel this in ways a MacBook will not.

**The API surface is evolving fast.** The docs do not always reflect current method names in mlx-swift and swift-transformers. Pin your versions explicitly and check the respective repositories before assuming any snippet compiles as-is.

**Profile time-to-first-token, not throughput.** For interactive applications, TTFT is the user-perceived metric that matters. This is where MLX's lazy graph evaluation delivers its most visible win over CoreML's eager dispatch.

## When CoreML Still Wins

MLX is not a universal replacement. CoreML retains the edge when your model ships statically with the app, when you need ANE efficiency for battery-sensitive sustained inference, or when you are targeting older A-series chips. The right production answer is usually both β€” CoreML for static, battery-sensitive workloads; MLX for dynamic fine-tuned models updating from a server on macOS.

## Conclusion

Three things to take away:

1. **Convert weights to `.safetensors` via mlx-lm once.** It eliminates the CoreML compilation bottleneck from your iteration cycle and keeps your checkpoint portable across chip generations.
2. **Wrap your KV-cache in a Swift 6 actor.** Data-race safety at negligible runtime cost.
3. **Benchmark time-to-first-token before committing to an inference backend.** It is the metric that matters for interactive use, and it is where MLX's lazy evaluation delivers the most visible win.

---

**Resources:**
- [mlx-swift on GitHub](https://github.com/ml-explore/mlx-swift)
- [swift-transformers on GitHub](https://github.com/huggingface/swift-transformers)
- [mlx-lm Python toolchain](https://github.com/ml-explore/mlx-examples/tree/main/llms)
── more in #machine-learning 4 stories Β· sorted by recency
── more on @mlx 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/wiring-mlx-to-swift-…] indexed:0 read:5min 2026-08-19 Β· β€”