> ## Documentation Index
> Fetch the complete documentation index at: https://developers.namuai-lab.com/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS (Swift)

> Record, transcribe and speak from an iPhone app, with the key safe on your server.

10 min

Voice in an iPhone app

The app talks to your server; your server holds the Namu key and calls the API. Use the server routes from the [Android page](https://developers.namuai-lab.com/sdks/android.md#1-the-server-route): they serve both apps.

```text theme={null}
iPhone app  ──(your login)──►  your server  ──(NAMU_API_KEY)──►  api.namuai-lab.com
```

## 1. Record

`AVAudioRecorder` writes AAC in an M4A file, which the API reads as it is. Add `NSMicrophoneUsageDescription` to your `Info.plist`.

```swift Recorder.swift theme={null}
import AVFoundation

final class Recorder {
    private var recorder: AVAudioRecorder?
    let url = FileManager.default.temporaryDirectory.appendingPathComponent("note.m4a")

    func start() throws {
        let session = AVAudioSession.sharedInstance()
        try session.setCategory(.playAndRecord, mode: .spokenAudio, options: [.defaultToSpeaker])
        try session.setActive(true)
        recorder = try AVAudioRecorder(url: url, settings: [
            AVFormatIDKey: kAudioFormatMPEG4AAC,
            AVSampleRateKey: 16_000,
            AVNumberOfChannelsKey: 1,          // mono: half the bytes, same words
            AVEncoderBitRateKey: 32_000,
        ])
        recorder?.record()
    }

    func stop() { recorder?.stop() }
}
```

## 2. Send it to your server

```swift VoiceAPI.swift theme={null}
import Foundation

struct VoiceAPI {
    let baseURL: URL
    let token: String          // your user's token, not the Namu key

    private var session: URLSession {
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 300   // a model waking up can take a minute
        config.waitsForConnectivity = true       // wait for the network instead of failing at once
        return URLSession(configuration: config)
    }

    func transcribe(_ file: URL, language: String = "ha") async throws -> String {
        let boundary = UUID().uuidString
        var body = Data()
        func field(_ name: String, _ value: String) {
            body.append("--\(boundary)\r\nContent-Disposition: form-data; name=\"\(name)\"\r\n\r\n\(value)\r\n".data(using: .utf8)!)
        }
        field("language", language)
        body.append("--\(boundary)\r\nContent-Disposition: form-data; name=\"file\"; filename=\"note.m4a\"\r\nContent-Type: audio/mp4\r\n\r\n".data(using: .utf8)!)
        body.append(try Data(contentsOf: file))
        body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

        var request = URLRequest(url: baseURL.appendingPathComponent("api/transcribe"))
        request.httpMethod = "POST"
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        let (data, response) = try await session.upload(for: request, from: body)
        let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
        guard (response as? HTTPURLResponse)?.statusCode == 200, let text = json["text"] as? String else {
            throw URLError(.badServerResponse)
        }
        return text
    }

    func speak(_ text: String) async throws -> Data {
        var request = URLRequest(url: baseURL.appendingPathComponent("api/speak"))
        request.httpMethod = "POST"
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try JSONSerialization.data(withJSONObject: ["text": text, "voice": "nadia"])
        let (data, response) = try await session.data(for: request)
        guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
        return data   // MP3
    }
}
```

## 3. Play the answer

```swift theme={null}
import AVFoundation

var player: AVAudioPlayer?   // keep a reference, or playback stops at once

let mp3 = try await api.speak("Na gode! Mun karɓi saƙonku.")
player = try AVAudioPlayer(data: mp3)
player?.play()
```

Keep `note.m4a` until the transcription succeeds, so a dropped connection never means asking the person to speak again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.