iOS Low Latency PTT SDK
SDK Version: 1.0.0
Low Latency PTT provides half-duplex live voice over a dedicated RF mode. The LowLatencyPtt Swift product owns audio capture/playback, MELP1200 packetization, RF activation and verified restoration, group isolation, bounded queues, state, events, and statistics.
Add the delivered product and permission
Commercial customers who receive the aggregate binary kit must add the
delivered XTalkCommercialSDK.xcframework to the application target:
- Add the XCFramework under Frameworks, Libraries, and Embedded Content.
- It is a static XCFramework, so select Do Not Embed.
- Import the aggregate module:
import XTalkCommercialSDK
The aggregate module directly exposes LowLatencyPttSdk,
LowLatencyPttConfiguration, LowLatencyPttGroupProfile, and the other
public PTT types used below. Do not also link the standalone
LowLatencyPtt product because that would mix two delivery boundaries and
may duplicate binary content.
Use the standalone import below only when the customer delivery manifest
explicitly includes an exact-version Swift Package product named
LowLatencyPtt:
import LowLatencyPtt
The import must match the delivered artifact; do not switch delivery forms
based only on a website sample. In either form, do not import codec-support
targets. Add NSMicrophoneUsageDescription and request microphone access
before startTalking().
Configure and create
let operations = try LowLatencyPttGroupProfile.group(
groupId: "ops",
title: "Operations",
channel: 7,
key4: "7007"
)
let config = try LowLatencyPttConfiguration(
channel: .default,
rateMode: .rate7,
availableGroups: [.everyone, operations],
selectedGroupId: LowLatencyPttGroupProfile.everyone.id
)
let sdk = LowLatencyPttSdk(transport: transport, configuration: config)
The fixed profile contains 16 physical channels. Rates are .rate6 and .rate7. A private group requires a printable ID, group channel 1...161, and a four-digit ASCII key. The group channel contributes to key derivation and does not select physical RF.
Transport contract
Implement LowLatencyPttTransport.acquireExclusiveSession(). A LowLatencyPttTransportSession supplies stable identity, isReady, readiness AsyncStream<Bool>, deviceDid, executeAt, sendRfNoWait, and idempotent release. All AT and RF operations from acquisition through restoration must use the same underlying BLE/UART object.
Observe and control
LowLatencyPttSdk is a @MainActor ObservableObject. Read snapshot or observe $snapshot; transient events arrive through events.
let eventTask = Task { @MainActor in
for await event in sdk.events { handle(event) }
}
do {
try await sdk.enable()
try await sdk.startTalking()
try await sdk.stopTalking()
try await sdk.disable()
try await sdk.close()
} catch let error as LowLatencyPttError {
handle(error)
}
eventTask.cancel()
Controls are enable, disable, startTalking, stopTalking, setChannel, setRateMode, setAvailableGroups, setGroup(id:), and close.
Snapshot states are disabled, preparing, idle, talking, receiving, error, and terminal closed. Snapshot fields include enabled/restore flags, selected channel/rate/group, available groups, statistics, and last error. Events report state change, failure, group change, and receive rejection.
Route RF input
let consumed = sdk.tryHandleInbound(payload, rssi: rssi, snr: snr)
if !consumed { otherRadioProtocols.accept(payload) }
The nonisolated method should be the first RF consumer. It returns false for disabled/closed or unrelated packets. Candidate PTT frames remain consumed even when a later checksum, synchronization, group, rate, echo, duplicate, or queue test rejects them.
RF and audio behavior
enable() acquires one session, reads original FREQ/RATE/BCNID, writes and verifies the selected PTT triple, then initializes audio. disable()/close() restore and verify the original triple before release.
Audio is 8 kHz mono PCM16 little-endian. RATE7 uses two MELP frames per packet; RATE6 uses four. Full frames are 31/53 bytes and compact frames are 25/47 bytes. Android and iOS peers must use the same rate and protocol generation.
Errors and recovery
Domain errors are notConnected, permissionDenied, busy, invalidConfiguration, rfConfigurationFailed, audioFailed, sendFailed, restoreFailed, and closed. Configuration factories throw validation errors; task cancellation remains CancellationError.
When snapshot.restorePending is true, the host must not switch or reassign the radio. Retry disable or use close recovery. A closed snapshot with restoration still pending requires an external disconnect/reconnect and RF verification before reuse.
Private-group four-digit keys provide product group isolation, not high-strength end-to-end identity authentication. Never log the original key or derived material.