How to Build an Instrument Tuner App in SwiftUI
An Instrument Tuner app listens through the microphone, identifies the nearest musical pitch in real time, and shows musicians how many cents sharp or flat they are. It's the perfect intermediate SwiftUI project for developers who want hands-on experience with AVFoundation audio pipelines and Accelerate FFT on iOS.
Prerequisites
- Xcode 16 or later installed with the iOS 17 SDK
- An Apple Developer account (free tier works for Simulator; device testing and App Store require paid membership)
- Basic familiarity with SwiftUI views,
@StateObject, and theAVFoundationframework
Step-by-Step Guide
1. Define the Data Model
Use a SwiftData @Model class to persist each tuning session so users can review historical accuracy over time.
import SwiftData
import Foundation
@Model
final class TunerSession {
var instrument: String
var detectedNote: String
var centOffset: Double
var timestamp: Date = Date()
init(instrument: String, note: String, cents: Double) {
self.instrument = instrument
self.detectedNote = note
self.centOffset = cents
}
}
2. Build the Core Tuner UI
Create a TunerView that displays the detected note name, a needle gauge, and a colour-coded cent readout that turns green when the player is within ±5 cents.
struct TunerView: View {
@StateObject private var tuner = TunerEngine()
var body: some View {
VStack(spacing: 24) {
Text(tuner.currentNote)
.font(.system(size: 72, weight: .bold, design: .rounded))
TunerNeedle(cents: tuner.centOffset)
.frame(height: 160)
Text(String(format: "%+.1f ¢", tuner.centOffset))
.foregroundStyle(abs(tuner.centOffset) < 5 ? .green : .red)
}
.onAppear { tuner.start() }.onDisappear { tuner.stop() }
}
}
3. Implement Pitch Detection with AVFoundation & Accelerate
Wire up the device microphone with AVAudioEngine, tap a 4096-sample buffer, and run an Accelerate FFT to locate the dominant frequency bin on a background thread.
final class TunerEngine: ObservableObject {
@Published var currentNote = "–"
@Published var centOffset: Double = 0
private let engine = AVAudioEngine()
func start() {
let node = engine.inputNode
node.installTap(onBus: 0, bufferSize: 4096,
format: node.outputFormat(forBus: 0)) { [weak self] buf, _ in
self?.detectPitch(buffer: buf)
}
try? engine.start()
}
func stop() { engine.stop() }
}
Common Pitfalls
- Missing microphone usage description: Add
NSMicrophoneUsageDescriptiontoInfo.plistor App Review will reject your binary on the first pass. - FFT bin resolution too coarse: A 4096-sample window at 44.1 kHz gives ~10.8 Hz/bin — fine for most notes above A2, but bass instruments need a larger buffer or a polyphase approach to avoid misreads.
- UI updates off the main thread: Audio tap callbacks run on a background queue; always dispatch
@Publishedwrites toDispatchQueue.mainto avoid SwiftUI runtime warnings.
Monetization: One-Time Purchase
A one-time purchase is the cleanest fit for a tuner — musicians hate recurring fees for a utility they've owned for years. Implement it with StoreKit 2's Product.purchase() API: gate advanced features (chromatic history, custom temperaments, or an ad-free experience) behind a single non-consumable in-app purchase. StoreKit 2 handles receipt verification server-side, so you need zero backend infrastructure; just check Transaction.currentEntitlements on app launch to restore purchases automatically across the user's devices.
Ship Faster with Soarias
Soarias scaffolds your entire Instrument Tuner project — AVAudioEngine tap setup, Accelerate FFT boilerplate, SwiftData schema, StoreKit 2 purchase flow, and Fastlane lanes for screenshots and App Store submission — in minutes instead of days. The one-time $79 desktop app runs locally alongside Claude Code, so your source code never leaves your machine while Soarias handles the tedious wiring that would otherwise eat your weekend.
Related Tutorials
FAQ
Do I need an Apple Developer account?
You can build and run the tuner on the iOS Simulator with a free Apple ID. However, testing real microphone input requires a physical device, and distributing on the App Store requires a paid Apple Developer Program membership ($99/year).
How do I submit to the App Store?
Archive your app in Xcode (Product › Archive), then use the Organizer to validate and upload to App Store Connect. Fill in your app's metadata, screenshots, and privacy labels, set your one-time purchase price, and submit for review — Apple's typical review time is 24–48 hours for new apps.
Last reviewed: 2026-05-12 by the Soarias team.