iOS:从通知扩展播放远程音频文件

Posted

技术标签:

【中文标题】iOS:从通知扩展播放远程音频文件【英文标题】:iOS: play remote audio file from notification extension 【发布时间】:2018-01-22 10:46:03 【问题描述】:

我正在尝试通过通知扩展播放远程音频文件

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) 
    //...
    let url = URL(string: "https://firebasestorage.googleapis.com/v0/b/XXX.appspot.com/o/XXXXXXX?alt=media&token=XXXXXXX")
    let item = AVPlayerItem(url: url!)
    NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
    let player = AVPlayer(playerItem: item)
    player.play()

但是什么也没发生。

在应用程序的功能中,我启用了以下后台模式:“音频、AirPlay 和 PiP”、“后台获取”、“远程通知”

我需要在通知到达时播放声音而无需用户交互。 此外,媒体附件不是一个选项,因为 ios 10 支持是强制性的。

UPD:我试过了

func download(url: URL) 
    URLSession.shared.downloadTask(with: url, completionHandler:  _, _, error in
        if let error = error 
            print(error)
        
        self.play(url: url)
    ).resume()


func play(url: URL) 
    let player = try! AVAudioPlayer(contentsOf: url)
    player.prepareToPlay()
    player.volume = 1.0
    player.play()

什么也没发生

【问题讨论】:

我不知道你所说的“从通知扩展播放远程音频文件”是什么意思,但你应该使用 URLSessionDownloadTask。 @ElTomato 我刚才试过了,不行 【参考方案1】:

iOS 13 现在可以做到这一点。我将在此处复制消息的内容,以防它被删除。 https://forums.developer.apple.com/thread/118404Correct

KevinE 于 2019 年 7 月 30 日下午 5:11 回答

您的应用应该如何工作的细节会有很大的不同 取决于您的具体用例和应用程序设计,但我们一般 对一键通应用程序的建议是您转向使用 标准推送通知而不是 PushKit 用于消息传递。 更具体地说,在接收方您的通知服务 扩展程序应下载相关音频并将该声音附加到 您的特定信息。

为了支持这种方法,从 iOS 13 开始,系统会寻找 应用程序组容器中可能的声音文件以及 预先存在的搜索位置。文档没有更新 为此尚未(),但有头文件文档描述 "UNNotificationSound.h" 中 "soundNamed:" 的 cmets 中的详细信息:

// 通知播放的声音文件。声音必须是 在应用程序数据容器的 Library/Sounds 文件夹中或 应用组数据容器的 Library/Sounds 文件夹。如果文件是 在容器中找不到,系统将在应用程序包中查找。

详细分解,我们在下面查找声音文件 顺序和地点:

“库/声音”中的应用直接容器。您的应用组 名为“Library/Sounds”的目录中的目录 您的应用程序包 这里要记住的主要事项:

目录 #2 是“库/声音”,而不仅仅是“声音”。你需要 创建一个“库”目录,其中包含“声音”目录,而不是 只是一个“声音”目录。应用程序直接容器仍然是 首先我们会看,所以你需要小心你如何 命名您的文件。我建议使用组目录 对于所有声音或遵循两种不同的命名约定 位置,使它们永远不会发生碰撞。您的网络服务扩展 看不到目录#1的内容,所以不知道是否 不会有一个特定的名称发生冲突。

【讨论】:

【参考方案2】:

感谢TALE的回答,我已经实现了

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler 
    self.receivedRequest = request;
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    if (request.content.userInfo != nil && request.content.userInfo[@"audioUrl"] != nil) 
        NSURL* audioUrl = [NSURL URLWithString:request.content.userInfo[@"audioUrl"]];
        NSString* audioName = audioUrl.lastPathComponent;
        [[[NSURLSession sharedSession] downloadTaskWithURL:audioUrl completionHandler: ^void (NSURL* location, NSURLResponse* response, NSError* error) 
            if (location == nil) 
                contentHandler(self.bestAttemptContent);
                NSLog(@"%@", error);
                return;
            
            NSFileManager* fileMan = [NSFileManager defaultManager];
            NSString* containerPath = [fileMan containerURLForSecurityApplicationGroupIdentifier:@"group.com.example"].path; // Put your app group id here
            NSString* soundDir = [containerPath stringByAppendingString:@"/Library/Sounds"];
            NSError* errorOut;
            if (![fileMan fileExistsAtPath:soundDir]) 
                [fileMan createDirectoryAtPath:soundDir withIntermediateDirectories:YES attributes:nil error:&errorOut];
             else 
                // Delete old files
                NSArray<NSString*>* oldFiles = [fileMan contentsOfDirectoryAtPath:soundDir error:&errorOut];
                if (oldFiles != nil) 
                    for (NSString* oldFileName in oldFiles) 
                        NSString* oldFilePath = [soundDir stringByAppendingString:oldFileName];
                        [fileMan removeItemAtPath:oldFilePath error:&errorOut];
                    
                
            
            NSString* soundPath = [soundDir stringByAppendingString:[@"/" stringByAppendingString:audioName]];
            NSURL* soundUrl = [NSURL fileURLWithPath:soundPath];
            [fileMan copyItemAtURL:location toURL:soundUrl error:&errorOut];
            UNNotificationSound* sound = [UNNotificationSound soundNamed:audioName];
            self.bestAttemptContent.sound = sound;
            contentHandler(self.bestAttemptContent);
        ] resume];
        return;
    
    
    // Your logic for non-audio notification
    
    self.contentHandler(self.bestAttemptContent);

如果您需要 Swift 版本,请随时发表评论

【讨论】:

【参考方案3】:

我认为,根据我之前的经验,我们可以在收到通知时播放捆绑声音文件。这意味着,我们必须将声音文件放在应用程序包中,并且声音文件是应用程序的预定义资源。

如果您想播放不同的声音,请在应用程序包中添加所有声音文件并使用通知有效负载密钥识别/播放。

例如,如果您想播放带有相关通知的 ABC 文件,则使用 Notification Payload 设置适当的逻辑/密钥并识别它。

请告诉我你的想法。快乐编码:)

其他参考: Play notification sound using URL

如果您想使用 URL 播放声音。然后你可以下载这个文件,也可以在缓冲特定文件后播放:

How to play mp3 audio from URL in ios swift

【讨论】:

请阅读问题标题。它清楚地写着“远程音频文件”。现在它被硬编码用于测试,但后来它会是音频消息,所以我不能把它们放在 bundle 中 是的,我已经仔细阅读了。我已经告诉过您,无法直接使用带有通知的 url 播放音频文件。有两种解决方案 1.您需要下载文件然后才能播放。请查看此网址:***.com/questions/34563329/…。 2.将 Predifine 文件与 app bundle 一起使用。 @DimaRostopira 嗨亲爱的。我需要你的帮助。 @EktaPadaliya 怎么了? @DimaRostopira 我想在推送接收时流式传输音频。那我该怎么做呢?

以上是关于iOS:从通知扩展播放远程音频文件的主要内容,如果未能解决你的问题,请参考以下文章

播放存储在谷歌驱动器中的音频文件(远程 URL) - Swift

通知 ios 上的自定义音频播放器控件

从远程 url 播放音频文件时如何生成音频频谱?

从通知控制中心同时播放多个音频

播放 iOS 的远程音频缓冲区时如何避免机器人声音?

如何在应用程序处于后台模式时在 iOS 10 中播放自定义音频