Skip to content

Web Speech Recognition API not working in Apple Devices

I have implemented the voice search functionality in Magento 2 website.

It’s working fine in the Android devices, but not working in the Apple devices.

When I click on button to record voice, It’s asking for microphone permission and after allow microphone it’s not ending speech recognition.

(function () {
'use strict';
$.widget('mage.voiceSearch', {
    classValues: {
        voiceSearchInput: '#search',
    },
    speechRecognition: null,
    isVoiceSearchActive: false,
    create: function() {
        const SPEECHRECOGNITION = window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition || window.oSpeechRecognition;
        if (typeof SPEECHRECOGNITION === "undefined") {
            $("#search-voice-input").hide();
        } else {
            this.initializeWebSpeechApi();
        }

        $('#search').on('blur', function(){
            if ($(this).val() !== '') {
                $('#search').addClass('active');
            }
            else {
                if ($('#typing-text-search').length > 0) {                    
                    $('p.typewriter').css("display","block");
                    $('#search').removeClass('active');
                }
                else {
                    $('#search').addClass('active');
                }
            }
        });
    },
    initializeWebSpeechApi: function() {
        let webSpeechApi = window.SpeechRecognition || window.webkitSpeechRecognition;
        this.speechRecognition = new webSpeechApi();
        this.speechRecognition.lang = window.getStoreLangCode;
        this.speechRecognition.onaudioend = this.finishCaptureAudio.bind(this);
        this.speechRecognition.onend = this.disconnectVoiceRecognizeService.bind(this);
        this.speechRecognition.onerror = this.apiErrors.bind(this);
        this.speechRecognition.onnomatch = this.noSignificantRecognition.bind(this);
        this.speechRecognition.onresult = this.readyToConnectApp.bind(this);
        this.speechRecognition.onspeechend = this.stopSpeechRecognitionService.bind(this);
        this.element.on({
            click: this.voiceSearchButtonClick.bind(this),
            mousedown: this.mouseMoveDownAction.bind(this),
        });
    },
    mouseMoveDownAction: function(event) {
        event.preventDefault();
    },
    voiceSearchButtonClick: function(event) {
        $(this.classValues.voiceSearchInput).val('');
        if ($('#typing-text-search').length > 0) {                    
            $('#typing-text-search').css("display","none");
        }
        $("#search-voice-input").attr('disabled', true);
        if (this.isVoiceSearchActive) {
            this.stopWebSpeechRecognization();
        } else {
            this.startWebSpeechRecognization();
        }
    },
    startWebSpeechRecognization: function() {
        this.speechRecognition.start();
        this.isVoiceSearchActive = true;
        $('#search').blur();
        $('#search').removeClass('active');
        if ($('#typing-text-search').length > 0) {                    
            $('#typing-text-search').css("display","none");
        }            
        $('#voice-search-label').css("display","block");
    },
    stopWebSpeechRecognization: function() {
        this.speechRecognition.stop();
        this.isVoiceSearchActive = false;
    },
    finishCaptureAudio: function() {
        $('#voice-search-label').css("display","none");
    },
    disconnectVoiceRecognizeService: function() {
        this.stopWebSpeechRecognization();
    },
    apiErrors: function(event) {
        let webSpeechErrorCode = event.error,
            consoleLogError = false,
            messageText = '';
        switch (webSpeechErrorCode) {
            case 'no-speech':
                messageText = 'No speech was detected.';
                break;
            case 'not-allowed':
                messageText = 'The user agent disallowed any speech input from occurring for reasons of security, privacy or user preference.';
                break;
            case 'language-not-supported':
                messageText = 'The language was not supported';
                break;
            case 'aborted':
                messageText = 'Speech input was aborted in some manner, perhaps by some user-agent-specific behavior like a button the user can press to cancel speech input.';
                break;
            case 'audio-capture':
                messageText = 'Audio capture failed.';
                break;
            case 'network':
                messageText = 'Network communication required for completing the recognition failed.';
                break;
            case 'service-not-allowed':
                messageText = 'The user agent disallowed the requested speech recognition service, either because the user agent doesn't support it or because of reasons of security, privacy or user preference. In this case it would allow another more suitable speech recognition service to be used instead';
                break;
            case 'bad-grammar':
                messageText = 'There was an error in the speech recognition grammar or semantic tags, or the chosen grammar format or semantic tag format was unsupported';
                break;
            default:
                messageText = 'WebSpeech API Internal Error';
                consoleLogError = true;
                break;
        }
        if (consoleLogError && event.message) {
            console.error(event.message);
        }
        this.errorMessageDisplay(messageText);
    },
    noSignificantRecognition: function(event) {
        this.errorMessageDisplay("Can't identify the  phrase or word");
    },
    readyToConnectApp: function(event) {
        let lastIndex = event.results.length - 1,
            finilzedVoiceInput = event.results[lastIndex][0].transcript;
        if (finilzedVoiceInput) {
            let $searchInput = $(this.classValues.voiceSearchInput)
            $searchInput.val(finilzedVoiceInput);
            $("#search-voice-input").removeAttr('disabled');
            $searchInput.trigger('input');
            document.getElementById("search").focus();
        } else {
            this.noSignificantRecognition(event);
        }
    },
    stopSpeechRecognitionService: function() {
        this.stopWebSpeechRecognization();
    },
    errorMessageDisplay: function(message) {
        if ($('#typing-text-search').length > 0) {                    
            $('p.typewriter').css("display","block");
            $('#search').removeClass('active');
        }
        else {
            $('#search').addClass('active');
        }
        $("#search-voice-input").removeAttr('disabled');
        customerData.set('messages', {
            messages: [{
                type: 'error',
                text: message
            }]
        });
        const collection = document.getElementsByClassName("message-error");
        this.fade(collection[0]);
    },
    fade(element) {
        var op = 1;
        var timer = setInterval(function () {
            if (op <= 0.3) {
                clearInterval(timer);
                element.style.display = 'none';
            }
            element.style.opacity = op;
            element.style.filter = 'alpha(opacity=' + op * 100 + ")";
            op -= op * 0.2;
        }, 400);
    }
});
return $.mage.voiceSearch;
})();