Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Capturing stereo audio from built-In microphones

At a glance

Item Summary
Purpose Selects built-in microphone data sources and polar patterns, then records, meters, plays, and shares stereo audio.
App architecture A UIKit controller owns an audio façade and delegates metering and playback completion to focused collaborators.
Main patterns Controller/service split, weak delegate, protocol adapter, explicit state enum, value model for stereo layout.
Project style Single target grouped into Audio, Model, Extensions, and custom view files.

Project structure

StereoAudioCapture/
├── MainViewController.swift
├── Audio/
│   ├── AudioController.swift
│   └── MeterTable.swift
├── Model/StereoLayout.swift
├── StereoLevelMeterView.swift
├── StereoLayoutView.swift
└── Extensions/
    ├── FileManagerExtensions.swift
    └── UIViewExtensions.swift

Structure observations

  • AudioController is the single boundary around audio session, recorder, player, and file state.
  • Meter rendering and microphone-layout rendering are separate custom views; StereoLayout translates framework orientations into display cases.

Overall architecture

Reference code

StereoAudioCapture/Audio/AudioController.swift:134 — the façade resolves a UI option into an AVAudioSession data source and stereo orientation.

func selectRecordingOption(_ option: RecordingOption,
                           orientation: Orientation,
                           completion: (StereoLayout) -> Void) {
    let session = AVAudioSession.sharedInstance()
    guard let preferredInput = session.preferredInput,
          let dataSources = preferredInput.dataSources,
          let newDataSource = dataSources.first(where: { $0.dataSourceName == option.dataSourceName })
    else { return }
    // Configure the selected source and report its layout.
}

Interpretation

The UIKit controller maps buttons and orientation to coarse audio commands. AudioController owns framework state and produces a semantic StereoLayout; the meter only knows the small StereoLevelsProvider capability.

Ownership and state

Ownership evidence

StereoAudioCapture/MainViewController.swift:15 — the screen creates its audio service and supplies it to the meter/delegate relationships.

class MainViewController: UIViewController {
    // ...
}
Owner Object or state Relationship Mutation authority
MainViewController AudioController and screen state Creates and issues user commands Controller for audio; view controller for UI
AudioController Recorder, player, URL, timer, state Creates/retains and coordinates AudioController
StereoLevelMeterView Display link and child meters Creates and renders Meter view
StereoLayoutView Layout image Retains view state Layout view from semantic model

Class and protocol design

Type Responsibility Depends on
MainViewController Translate UIKit events to audio commands and present results AudioController
AudioController Configure session, record/play, persist, and expose levels AVFAudio APIs
StereoLevelsProvider Read-only stereo meter capability StereoChannelLevels
Meterable Normalize recorder/player meter APIs AVAudioRecorder, AVAudioPlayer conformances
StereoLayout Map physical orientation and source orientation to a UI case AVAudioSession orientation enums

Access control

Symbol Access Verified effect Likely rationale
AudioController.state private(set) UI can inspect state but only controller operations change it. Preserve the record/play state machine.
Recorder/player/URL/timer private Callers cannot manipulate framework objects independently. Keep lifecycle transitions atomic at the façade.
RecordingOption.dataSourceName fileprivate Display name is broadly readable; hardware lookup key stays in AudioController.swift. Prevent UI dependence on session identifiers.
Orientation.inputOrientation fileprivate extension Conversion is usable only by audio-controller code in this file. Localize framework mapping.
LevelMeterView private nested-file type Only the stereo meter composes raw channel meters. Hide a rendering implementation detail.
App types implicit internal Available only inside the app module. No framework API is exported.

Reference code

StereoAudioCapture/Audio/AudioController.swift:38 — the model exposes a title but confines its device key to this file.

struct RecordingOption: Comparable {
    let name: String
    fileprivate let dataSourceName: String
}

Logic ownership and placement

Logic Owning type or file Placement rationale
UI state and actions MainViewController UIKit screen concern.
Session/source/polar-pattern selection AudioController Coupled framework configuration.
Recording and playback lifecycle AudioController Same owner as state and file URL.
dB-to-linear conversion MeterTable Focused numerical helper.
Level visualization StereoLevelMeterView Rendering/display-link concern.
Physical layout mapping StereoLayout Pure domain translation.

Design patterns

Pattern Source evidence Purpose or tradeoff
Façade/controller StereoAudioCapture/Audio/AudioController.swift:46 Gives the screen one coherent audio boundary.
Weak delegate StereoAudioCapture/Audio/AudioController.swift:83 Reports playback completion without retaining the screen.
Protocol adapter StereoAudioCapture/Audio/AudioController.swift:282 Treats recorder and player uniformly for metering.
Read-only provider StereoAudioCapture/StereoLevelMeterView.swift:23 Decouples the meter from audio lifecycle details.
Explicit state model StereoAudioCapture/Audio/AudioController.swift:32 Makes stopped/playing/recording transitions visible.

Naming conventions

  • Coordinator names use role nouns: AudioController, MainViewController.
  • Capability protocols use provider/delegate suffixes: StereoLevelsProvider, AudioControllerDelegate.
  • Commands use concrete verbs: selectRecordingOption, setupRecorder, record, play.
  • UI files match their primary view; model and extension folders expose their purpose.

Architecture takeaways

  • Hide device-source identifiers while presenting a small display model to UI code.
  • Use protocols to normalize only the capability the consumer needs, such as metering.
  • Keep recorder, player, state, and persistence under one lifecycle authority.
  • Map hardware orientation into a semantic value before updating the view.

Source map

Source file Relevant symbols
StereoAudioCapture/MainViewController.swift Screen coordination and delegate conformance
StereoAudioCapture/Audio/AudioController.swift Session, recording, playback, protocols, state
StereoAudioCapture/Audio/MeterTable.swift Level conversion
StereoAudioCapture/StereoLevelMeterView.swift Meter provider and display
StereoAudioCapture/Model/StereoLayout.swift Layout mapping