iOS SDK

Updated at:

Use the Intelligent Speech Interaction iOS NUI SDK to integrate short sentence recognition into an iOS application and convert audio to text. The SDK provides initialization, recognition control, audio input, and result callbacks.

Prerequisites

  • Create a project and obtain its Appkey. For more information, see Manage projects.

  • Obtain a valid NLS token.

Note

For information about obtaining a token, see Obtain a token.

Important

Short sentence recognition uses an Appkey and an NLS token for authentication. In production, obtain and distribute tokens from your server. Do not store an AccessKey ID or AccessKey secret in application code or on a mobile device.

Download and integration

To download the SDK and demo project, see Select and download a mobile SDK.

iOS SDK versions after 2.5.14 use pure Objective-C interfaces instead of mixed Objective-C++ interfaces. The following integration instructions use the package structure of version 2.7.4.

  1. Extract the SDK package and add Release/nuisdk.xcframework to the Xcode project. Set it to Embed & Sign under General > Frameworks, Libraries, and Embedded Content, and confirm that the library is listed under Build Phases > Link Binary With Libraries. Do not link both the XCFramework and a standalone nuisdk.framework.

  2. Import the header in the source file:

    #import <nuisdk/NeoNui.h>
  3. To capture audio from the microphone, configure NSMicrophoneUsageDescription in Info.plist, and request and check microphone permission before recording.

  4. Refer to SpeechRecognizerViewController in the included demo to integrate the recognition workflow. The demo provides the AudioController recording module and utilities for operations such as file handling. Reuse or replace them as needed.

The XCFramework contains separate libraries for devices and simulators. The 2.7.4 package provides ios-arm64 and ios-arm64-simulator. Select a build destination that matches the target platform. The simulator library in this package supports the arm64 architecture.

Call sequence

  1. Create SDK and recorder instances, set their delegates, and initialize the SDK.

  2. Configure recognition parameters as needed.

  3. Call nui_dialog_start with MODE_P2T to start recognition.

  4. Control recording in onNuiAudioStateChanged and supply audio in onNuiNeedAudioData.

  5. If intermediate results are enabled, receive them through EVENT_ASR_PARTIAL_RESULT. Receive the final result through EVENT_ASR_RESULT.

  6. Call nui_dialog_cancel:NO to stop recognition and wait for the task to finish. If online VAD is enabled, the server can also determine when the sentence ends.

  7. Call nui_release when the SDK is no longer needed.

Important

Do not call potentially blocking initialization methods on the UI thread. To avoid deadlocks, do not synchronously call control methods such as initialization, start, cancel, or release from SDK event callbacks. Dispatch these operations to an application work queue instead.

Code examples

The following integration snippets are based on the demo included in version 2.7.4 and depend on its AudioController module. The recording module can be replaced to suit the application. The SDK package contains the complete demo project, UI, and recording module. The examples use 16 kHz, 16-bit, mono PCM audio. The recording configuration must match the recognition sample rate.

Initialize the SDK

Implement NeoNuiSdkDelegate and the recording module's ConvVoiceRecorderDelegate in the view controller. Retain the SDK instance, recorder instance, and audio buffer:

#import <nuisdk/NeoNui.h>
#import "audio/AudioController.h"

@interface SpeechRecognizerViewController () <NeoNuiSdkDelegate, ConvVoiceRecorderDelegate>
@property(nonatomic, strong) NeoNui *nui;
@property(nonatomic, strong) AudioController *audioController;
@property(nonatomic, strong) NSMutableData *recordedVoiceData;
@end

Use get_instance to obtain the singleton. If multiple instances are required, use [[NeoNui alloc] init]. Call nui_release before initializing the same instance again.

Configure the service URL:

NSString *gateway = @"wss://nls-gateway.cn-shanghai.aliyuncs.com:443/ws/v1";

Replace <Appkey> and <NLS Token> with the project Appkey and a valid token from your server. Initialize the SDK on a work queue:

_nui = [NeoNui get_instance];
_nui.delegate = self;
_recordedVoiceData = [NSMutableData data];
_audioController = [[AudioController alloc] init:only_recorder];
_audioController.delegate = self;

NSDictionary *initParams = @{
    @"app_key": @"<Appkey>",
    @"token": @"<NLS Token>",
    @"url": gateway,
    @"service_mode": @"4"
};
NSData *initData = [NSJSONSerialization dataWithJSONObject:initParams options:0 error:nil];
NSString *initJSON = [[NSString alloc] initWithData:initData encoding:NSUTF8StringEncoding];
NuiResultCode result = [_nui nui_initialize:initJSON.UTF8String
                                 logLevel:NUI_LOG_LEVEL_NONE
                                  saveLog:NO];
if (result != 0) {
    NSLog(@"nui_initialize failed: %d", result);
    return;
}

Starting with version 2.6.2, cloud-only features do not require workspace or device_id. For earlier versions that require workspace, set it to the resource path of Resources.bundle. The optional device_id helps locate issues and does not need to be an advertising identifier.

For debugging, set the log level to NUI_LOG_LEVEL_VERBOSE. When saveLog is enabled, set debug_path to a writable directory. Use save_wav to control whether debug audio is saved. Log files have no size limit. Clean them up regularly to avoid exhausting storage.

Set parameters

Set recognition parameters in JSON format. enable_intermediate_result is optional. Set it to YES if intermediate results are needed.

NSMutableDictionary *nlsConfig = [@{
    @"enable_intermediate_result": @YES,
    @"sample_rate": @16000,
    @"sr_format": @"pcm"
} mutableCopy];
NSDictionary *params = @{
    @"service_type": @(SERVICE_TYPE_ASR),
    @"nls_config": nlsConfig
};
NSData *paramsData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
NSString *paramsJSON = [[NSString alloc] initWithData:paramsData encoding:NSUTF8StringEncoding];
NuiResultCode result = [_nui nui_set_params:paramsJSON.UTF8String];
if (result != 0) {
    NSLog(@"nui_set_params failed: %d", result);
    return;
}

The 01B version does not include a local VAD module. The 029 version, which provides wake-up functionality, includes local VAD. To automatically detect the end of a sentence, add the following online VAD parameters after creating nlsConfig and before serializing it. Continue to use MODE_P2T when starting recognition:

nlsConfig[@"enable_voice_detection"] = @YES;
nlsConfig[@"max_start_silence"] = @10000;
nlsConfig[@"max_end_silence"] = @800;

For the meanings and values of parameters such as punctuation prediction, inverse text normalization, sample rate, and audio transmission format, see API reference.

If HTTPDNS is used, set the top-level direct_ip parameter to the resolved IP address. Use the top-level extend_config object to pass parameters that the service supports but that are not separately listed in the SDK.

Start recognition

NuiResultCode result = [_nui nui_dialog_start:MODE_P2T dialogParam:"{}"];
if (result != 0) {
    NSLog(@"nui_dialog_start failed: %d", result);
}

To update the Appkey or token for the current recognition task, include app_key or token in the dialogParam JSON string. If these temporary parameters are omitted from the next task, the values supplied during initialization are used.

Handle callbacks

Recording state and audio input

Start or stop recording based on the SDK audio state. Store audio from the recorder callback in the buffer, then supply it to the SDK through onNuiNeedAudioData.

- (void)onNuiAudioStateChanged:(NuiAudioState)state {
    if (state == STATE_OPEN) {
        @synchronized (self) {
            [_recordedVoiceData setLength:0];
        }
        [_audioController startRecorder];
    } else if (state == STATE_CLOSE || state == STATE_PAUSE) {
        [_audioController stopRecorder:NO];
    }
}

- (void)voiceDidFail:(NSError *)error {
    NSLog(@"Recording failed: %@", error.localizedDescription);
}

- (void)voiceRecorded:(unsigned char *)buffer Length:(int)len {
    @synchronized (self) {
        [_recordedVoiceData appendBytes:buffer length:(NSUInteger)len];
    }
}

- (int)onNuiNeedAudioData:(char *)audioData length:(int)len {
    @synchronized (self) {
        NSUInteger count = MIN(_recordedVoiceData.length, (NSUInteger)len);
        if (count == 0) return 0;
        [_recordedVoiceData getBytes:audioData length:count];
        [_recordedVoiceData replaceBytesInRange:NSMakeRange(0, count)
                                     withBytes:NULL length:0];
        return (int)count;
    }
}

Recognition results and errors

asr_result contains the recognition result or error information. Use task_id to help troubleshoot requests. A true finish value means the task has ended, either normally or because of an error.

- (void)onNuiEventCallback:(NuiCallbackEvent)event
                   dialog:(long)dialog
                kwsResult:(const char *)wuw
                asrResult:(const char *)asrResult
                 ifFinish:(BOOL)finish
                  retCode:(int)code {
    NSString *message = asrResult ? [NSString stringWithUTF8String:asrResult] : @"";
    if (event == EVENT_ASR_PARTIAL_RESULT || event == EVENT_ASR_RESULT) {
        NSLog(@"ASR result: %@", message);
    } else if (event == EVENT_ASR_ERROR || event == EVENT_MIC_ERROR) {
        NSLog(@"ASR error: %d, %@", code, message);
    }
    if (finish) {
        dispatch_async(dispatch_get_main_queue(), ^{
            // Update the UI to allow the next recognition task to start.
        });
    }
}

Stop recognition and release resources

For a normal stop, pass NO and wait for the final result and task completion callback. If the audio contains no valid speech, the final recognition text may be empty.

[_nui nui_dialog_cancel:NO];

If the result is no longer needed, call nui_dialog_cancel:YES to force cancellation and perform cleanup in the application. When the SDK is no longer needed, release resources and clear the delegates outside SDK callbacks:

[_audioController stopRecorder:NO];
[_nui nui_release];
_nui.delegate = nil;
_audioController.delegate = nil;

SDK interfaces and callbacks

Initialization and resource management

- (NuiResultCode)nui_initialize:(const char *)parameters
                      logLevel:(NuiSdkLogLevel)level
                       saveLog:(BOOL)save_log;
- (NuiResultCode)nui_release;
- (const char *)nui_get_version;

Method

Description

nui_initialize

Initializes the SDK with a JSON string. level sets the log level: lower values produce more output. save_log controls whether logs are saved. Release resources before initializing the same instance again.

nui_release

Releases SDK resources.

nui_get_version

Returns the SDK version as a string.

Recognition control

- (NuiResultCode)nui_set_params:(const char *)params;
- (NuiResultCode)nui_dialog_start:(NuiVadMode)vad_mode
                    dialogParam:(const char *)dialog_params;
- (NuiResultCode)nui_dialog_cancel:(BOOL)force;

Method

Description

nui_set_params

Sets recognition parameters with a JSON string.

nui_dialog_start

Starts recognition. Set vad_mode to MODE_P2T. Use dialog_params for temporary parameters for this task, or pass {} if none are needed.

nui_dialog_cancel

Stops recognition. force=NO stops and waits for the complete result. force=YES forces cancellation and ignores the final result.

The preceding methods that return NuiResultCode use 0 to indicate a successful call. Recognition results are delivered through event callbacks.

Event and audio callbacks

- (void)onNuiEventCallback:(NuiCallbackEvent)nuiEvent
                   dialog:(long)dialog
                kwsResult:(const char *)wuw
                asrResult:(const char *)asr_result
                 ifFinish:(BOOL)finish
                  retCode:(int)code;
- (int)onNuiNeedAudioData:(char *)audioData length:(int)len;
- (void)onNuiAudioStateChanged:(NuiAudioState)state;
- (void)onNuiRmsChanged:(float)rms;

In the event callback, asr_result provides recognition results or error information, finish indicates whether the task has ended, and code provides an error code. dialog and wuw are not used in this recognition scenario.

Event

Description

EVENT_VAD_START

The start of speech is detected.

EVENT_VAD_END

The end of speech is detected.

EVENT_ASR_PARTIAL_RESULT

An intermediate recognition result.

EVENT_ASR_RESULT

The final recognition result.

EVENT_ASR_ERROR

A recognition error. Check the error code and message.

EVENT_MIC_ERROR

A recording error. If no audio is received for 2 consecutive seconds, check the recorder and audio input.

  • onNuiNeedAudioData: Fill audioData with up to len bytes of audio and return the actual number of bytes supplied.

  • onNuiAudioStateChanged: Control recording based on STATE_OPEN, STATE_PAUSE, and STATE_CLOSE.

  • onNuiRmsChanged: Obtain the audio energy value, which ranges from -160 to 0.

To retrieve the full JSON information for the current event, use nui_get_all_response:

- (const char *)nui_get_all_response;

FAQ

What do I do if NeoNui.h cannot be found?

Confirm that the SDK is added to and linked with the project. Import the header with #import <nuisdk/NeoNui.h>, not #import "nuisdk.framework/Headers/NeoNui.h". Dynamic libraries must also be set to Embed & Sign.

Can mobile SDKs be used in a private cloud?

Mobile SDKs can call public cloud ASR and TTS services and can also be used in a private cloud. Private cloud installation packages do not include mobile SDKs by default. Download them from the documentation for the corresponding service, such as the Android SDK and iOS SDK for real-time speech recognition.

Is background processing supported?

The SDK itself does not restrict foreground or background use. Background recording also depends on the iOS audio session and application configuration. To record in the background:

  1. Add audio under Required background modes (UIBackgroundModes) in Info.plist. Its display value is App plays audio or streams audio/video using AirPlay.

  2. Check the recording module's background handling. Do not stop recording or deactivate the audio session when the application enters the background. In the older NLSVoiceRecorder.m module, remove the AudioSessionSetActive(NO) call from _appResignActive.

The 2.7.4 demo uses the AudioController module and configures UIBackgroundModes=audio. Its background handler does not call AudioSessionSetActive(NO).

What do I do if a device reports no suitable image found?

First confirm that the linked library targets a physical device. Then try deleting the application from the device, cleaning build artifacts, and reinstalling it. Also check that the application and dynamic library signatures, certificates, and provisioning profiles match. If the enterprise certificate is confirmed to be invalid, create a valid certificate and provisioning profile, then sign and package the application again.

What do I do if EVENT_MIC_ERROR occurs?

Check microphone permission, whether another module is using the recorder, and whether onNuiNeedAudioData continuously supplies audio. This event occurs if no audio data is supplied for 2 consecutive seconds.

What do I do if a library targets both iOS and iOS Simulator?

If the build reports Building for iOS, but the linked and embedded framework ... was built for iOS + iOS Simulator, check whether an older mixed-platform Framework is used. Integrate nuisdk.xcframework from the SDK package instead, so Xcode selects the library for the build destination. Do not embed a simulator library in a device application.

What do I do if App Store reports Unsupported Architectures?

If an older Framework contains simulator architectures such as x86_64 or i386, inspect it with lipo -info <Framework-binary> and remove simulator architectures from the device distribution package. When using an XCFramework, archive for a device destination so Xcode selects the device library.

What do I do if an older project requires Legacy Build System?

For older Xcode projects that still provide Validate Workspace, try setting it to Yes and rebuilding. New projects should integrate the XCFramework provided in the current SDK package.