Skip to content
Volume S · The TensorCore.si Library

Swift

Fast, safe and made for Apple platforms.

Swift is Apple's language for iOS, macOS, watchOS and visionOS apps. It is fast like C, safe by design, and Apple's MLX and Core ML let Swift apps run AI models right on the device.

Prologue

Why learn Swift?

If you want to build apps for the more than two billion active Apple devices, Swift is the language. SwiftUI makes building interfaces quick and visual.

  • Apple platforms: the native language for iOS and macOS apps.
  • Safe and fast: optionals prevent null crashes; code compiles to native speed.
  • On-device AI: Core ML, MLX Swift and Apple's Foundation Models framework run models locally.
Prologue

Set up and run your first program

On a Mac, install Xcode. On Linux or Windows, install the Swift toolchain from swift.org.

terminal
swift --version
swift hello.swift
hello.swift
let name = "Ada"
print("Hello, AI! Welcome to Swift, \(name).")
TipUse swift package init --type executable for a command-line project.
Chapter 1

let, var and optionals

let is a constant, var a variable. An optional (String?) may hold a value or nil; unwrap it with if let, guard let or ??.

variables.swift
let layers = 32
var lr = 3e-4
lr /= 2
let params: Int64 = 8_030_000_000
var note: String? = nil
print("\(layers) layers, lr \(lr), FP16 \(Double(params) * 2 / 1e9) GB")
print(note ?? "no note")
if let n = note { print(n.count) }
Chapter 2

Control flow

for-in loops over ranges and collections. switch must be exhaustive and does not fall through. guard exits early when a condition fails.

control.swift
for epoch in 0..<3 { print("epoch", epoch) }
let bits = 8
switch bits {
case 32: print("FP32")
case 16: print("FP16")
case 8:  print("FP8")
default: print("other")
}
func check(vram: Int?) {
    guard let v = vram, v >= 24 else { print("not enough VRAM"); return }
    print("ok with \(v) GB")
}
check(vram: 32)
Chapter 3

Functions and closures

Parameters have argument labels that make calls read like sentences. Closures are inline functions passed to map, filter and sorted.

functions.swift
func params(layers: Int, width: Int = 4096) -> Int {
    12 * layers * width * width
}
print(params(layers: 32))
let squares = (1...5).map { $0 * $0 }
let sorted = ["H200", "B200", "H100"].sorted()
print(squares, sorted)
Chapter 4

Structs, enums and protocols

Structs are value types and the default choice for data. Enums can carry values. Protocols define behaviour that types adopt.

types.swift
protocol Describable { var summary: String { get } }
enum Precision { case fp32, fp16, fp8 }
struct Gpu: Describable {
    let name: String
    let vramGb: Int
    var summary: String { "\(name): \(vramGb) GB" }
}
let gpus = [Gpu(name: "H100", vramGb: 80), Gpu(name: "B200", vramGb: 180)]
print(gpus.filter { $0.vramGb > 100 }.map(\.summary), Precision.fp8)
Chapter 5

async/await and errors

Functions that can fail are marked throws and called with try. Asynchronous work uses async/await, for example with URLSession.

async.swift
import Foundation

func modelNames() async throws -> String {
    let url = URL(string: "http://localhost:11434/api/tags")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return String(decoding: data, as: UTF8.self)
}
do {
    print(try await modelNames().prefix(120))
} catch {
    print("Is Ollama running? \(error)")
}
Part II · AI with Swift

AI with Swift

Swift is the way to bring AI into Apple apps. Core ML runs converted models with the Neural Engine, MLX Swift runs and fine-tunes LLMs on Apple silicon, and Apple's Foundation Models framework gives apps access to the on-device Apple Intelligence model.

The essential libraries

Core MLRun models on the Neural Engine, GPU and CPU.
MLX SwiftApple's array framework for LLMs on Apple silicon.
Foundation ModelsOn-device Apple Intelligence model with tool calling.
Create MLTrain image, text and sound models without code.

Gradient descent from scratch

Learn y = 3x + 0.5 with loops.

gradient.swift
let n = 100
let xs = (0..<n).map { -1 + 2 * Double($0) / Double(n - 1) }
let ys = xs.map { 3 * $0 + 0.5 + Double.random(in: -0.05...0.05) }
var w = 0.0, b = 0.0
let lr = 0.1
for _ in 0..<500 {
    var gw = 0.0, gb = 0.0
    for i in 0..<n {
        let err = w * xs[i] + b - ys[i]
        gw += 2 * err * xs[i] / Double(n); gb += 2 * err / Double(n)
    }
    w -= lr * gw; b -= lr * gb
}
print(String(format: "w=%.2f b=%.2f", w, b))
Part III · Build an AI agent

Build an AI agent in Swift

With URLSession and JSONSerialization you can run the tool-calling loop against a local model. On Apple devices, the Foundation Models framework offers built-in tool calling with the on-device model.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
agent.swift
import Foundation

func getWeather(_ city: String) -> String { "It is 24°C and sunny in \(city)." }
let tools: [[String: Any]] = [["type": "function", "function": [
    "name": "get_weather", "description": "Weather for a city",
    "parameters": ["type": "object", "properties": ["city": ["type": "string"]], "required": ["city"]]]]]
var messages: [[String: Any]] = [["role": "user", "content": "Should I take an umbrella in Lahore today?"]]

for _ in 0..<5 {
    var req = URLRequest(url: URL(string: "http://localhost:11434/v1/chat/completions")!)
    req.httpMethod = "POST"
    req.setValue("application/json", forHTTPHeaderField: "Content-Type")
    req.httpBody = try JSONSerialization.data(withJSONObject: ["model": "llama3.1", "messages": messages, "tools": tools])
    let (data, _) = try await URLSession.shared.data(for: req)
    let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
    let msg = ((json["choices"] as! [[String: Any]])[0]["message"]) as! [String: Any]
    messages.append(msg)
    guard let calls = msg["tool_calls"] as? [[String: Any]], !calls.isEmpty else {
        print(msg["content"] ?? ""); break
    }
    for call in calls {
        let fn = call["function"] as! [String: Any]
        let args = try JSONSerialization.jsonObject(with: Data((fn["arguments"] as! String).utf8)) as! [String: String]
        messages.append(["role": "tool", "tool_call_id": call["id"] ?? "", "content": getWeather(args["city"] ?? "")])
    }
}

In a real app, model messages with Codable structs instead of dictionaries.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Swift learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

SwiftUI to-do appLists, state and persistence.
Photo labellerClassify images with Core ML.
Private chat appRun a small LLM on device with MLX Swift.
Reference

Swift cheat sheet

TaskSyntax
Constantlet x = 1
Optionalvar s: String? = nil
Unwrapif let v = s { }
Interpolate"Hi \(name)"
Closurearr.map { $0 * 2 }
Asynclet d = try await f()
Reference

Common mistakes

  • Force-unwrapping with ! crashes on nil; unwrap safely.
  • Updating UI off the main actor causes warnings and bugs; mark UI code @MainActor.
  • Structs are copied: changing a copy does not change the original.
Reference

Where to go next

On TensorCore.si: Kotlin volume, What is an NPU?.

Keyboard: [ previous language · ] next language · / search the library