Воспроизведение звука в didReceiveRemoteNotification, в то время как в backgorund, использование функции "текст в речь"

Так что я пытаюсь в настоящее время играть сообщение, когда приложение получает удаленное уведомление в фоновом режиме (или, вероятно, проснувшись от приостановленного состояния).

Звук не воспроизводится вообще после того, как приложение пробуждается из приостановленного режима.

Когда приложение находится на переднем плане, звук воспроизводится сразу после didReceiveRemoteNotification: метода didReceiveRemoteNotification: method.

Каким будет подходящий способ немедленного воспроизведения звуков, когда метод didReceiveRemoteNotification: метод вызывается, когда приложение didReceiveRemoteNotification: из приостановленного режима?

Вот какой код (класс речевого менеджера):

-(void)textToSpeechWithMessage:(NSString*)message andLanguageCode:(NSString*)languageCode{

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
DLog(@"Activating audio session");
if (![audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:&error]) {
    DLog(@"Unable to set audio session category: %@", error);
}
BOOL result = [audioSession setActive:YES error:&error];
if (!result) {
    DLog(@"Error activating audio session: %@", error);

}else{
    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:message];

    [utterance setRate:0.5f];

    [utterance setVolume:0.8f];

    utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:languageCode];

    [self.synthesizer speakUtterance:utterance];
}

}

-(void)textToSpeechWithMessage:(NSString*)message{

[self textToSpeechWithMessage:message andLanguageCode:[[NSLocale preferredLanguages] objectAtIndex:0]];

}

И позже в AppDelegate:

[[MCSpeechManager sharedInstance] textToSpeechWithMessage:messageText];

Я включил функцию "Аудио", "AirPlay" и "Изображение в картинке" в разделе Capabilities-> "Режимы фона".

РЕДАКТИРОВАТЬ:

Может быть, мне нужно запустить фоновое задание и запустить обработчик срока действия, если это необходимо? Я думаю, это может сработать, но я также хотел бы услышать общий способ решения таких ситуаций.

Также с этим кодом я получаю следующую ошибку, когда получаю уведомление в фоновом режиме:

Ошибка при активации сеанса аудио: Ошибка домена = NSOSStatusErrorDomain Code = 561015905 "(null)"

Код 561015905 применяется к:

AVAudioSessionErrorCodeCannotStartPlaying = '! Pla',/* 0x21706C61, 561015905

И это описано как:

Этот тип ошибки может возникнуть, если список свойств информации приложений не позволяет использовать аудио, или если приложение находится в фоновом режиме и использует категорию, которая не позволяет использовать фоновый звук.

но я получаю ту же ошибку с другими категориями (AVAudioSessionCategoryAmbient и AVAudioSessionCategorySoloAmbient)

Ответ 1

Поскольку я не могу воспроизвести ошибку, которую вы описываете, позвольте мне предложить несколько указателей и некоторый код.

  • Вы строите/тестируете/работаете против последнего SDK? Существенные изменения вокруг механизма уведомления в iOS X
  • Я должен предположить, что обращение к didReceiveRemoteNotification должно происходить в ответ на действие пользователя из упомянутого уведомления, например, при нажатии на сообщение уведомления.
  • Нет необходимости устанавливать какие-либо фоновые режимы, чтобы сохранять содержимое загружаемых приложений в ответ на push-уведомления.

Если все приведенные выше утверждения верны, в настоящем ответе основное внимание будет уделено тому, что произойдет, когда поступит уведомление.

  1. Устройство получает уведомление
    Remote
  2. Пользователь нажимает на сообщение
  3. Запуск приложений
  4. didReceiveRemoteNotification

На шаге 4 textToSpeechWithMessage работает так, как ожидалось:

func application(_ application: UIApplication,
                 didReceiveRemoteNotification
                 userInfo: [AnyHashable : Any],
                 fetchCompletionHandler completionHandler:
                 @escaping (UIBackgroundFetchResult) -> Void) {
    textToSpeechWithMessage(message: "Speak up", "en-US")
}

Для простоты я использую OneSignal для подключения уведомлений:

import OneSignal
...
_ = OneSignal.init(launchOptions: launchOptions,
                   appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
// or
_ = OneSignal.init(launchOptions: launchOptions,
                   appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
                   {
                       (s:String?, t:[AnyHashable : Any]?, u:Bool) in
                       self.textToSpeechWithMessage(message: "OneDignal", "en-US")
                   }

textToSpeechWithMessage в основном нетронутый, вот он в Swift 3 для полноты:

import AVFoundation
...
let synthesizer = AVSpeechSynthesizer()
func textToSpeechWithMessage(message:String, _ languageCode:String)
{
    let audioSession = AVAudioSession.sharedInstance()

    print("Activating audio session")
    do {
        try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord,
                                     with: [AVAudioSessionCategoryOptions.defaultToSpeaker,
                                            AVAudioSessionCategoryOptions.mixWithOthers]
        )
        try audioSession.setActive(true)

        let utterance = AVSpeechUtterance(string:message)
        utterance.rate = 0.5
        utterance.volume = 0.8
        utterance.voice = AVSpeechSynthesisVoice(language: languageCode)
        self.synthesizer.speak(utterance)

    } catch {
        print("Unable to set audio session category: %@", error);
    }
}

Ответ 2

Внесите

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; 

метод. Вы получите обратный вызов в фоновом режиме для воспроизведения звука.