iOS實(shí)現(xiàn)自定義鈴聲(從VoIP到NSE)

一茸歧、背景

2018年,受通信監(jiān)管要求丈挟,蘋果國區(qū)AppStore不允許app支持CallKit刁卜。
2019年,隨著iOS 13正式開放曙咽, Xcode11隨之發(fā)布蛔趴,蘋果已不再允許將PushKit應(yīng)用在非VoIP語音通話的場(chǎng)景上,開發(fā)者必須在接入CallKit的情況下才能使用PushKit例朱。也就是說孝情,通過 Xcode 11 編譯的 iOS 13 系統(tǒng) App,如果使用 PushKit 則必須依賴蘋果 CallKit 才能正常接收VoIP推送洒嗤。
結(jié)論:使用VoIP功能則無法在中國區(qū)App Store上架箫荡。

二、技術(shù)方案

VoIP

以微聊音視頻鈴聲播放為例渔隶,在之前的方案中羔挡,微聊音視頻消息會(huì)使用VoIP Push Notification,客戶端在被喚醒之后將獲得30s的后臺(tái)運(yùn)行時(shí)間间唉,在這30s內(nèi)绞灼,微聊被喚醒調(diào)起音視頻頁面,并播放定制鈴聲音頻呈野。

Notification Service Extension

新的方案是主要是利用了蘋果在iOS10中推出的Notification Service Extension(以下簡(jiǎn)稱NSE)低矮,當(dāng)apns的payload上帶上"mutable-content"的值為1時(shí),就會(huì)進(jìn)入NSE的代碼中际跪。與Voip方案最大的不同之處是商佛,NSE不能喚醒主應(yīng)用喉钢,也不能訪問主應(yīng)用的文件空間,只能在Extension進(jìn)程中處理相應(yīng)的邏輯良姆。在NSE中肠虽,開發(fā)者可以更改通知的內(nèi)容,利用離線合成或者從后臺(tái)下載的方式玛追,生成需要播報(bào)的內(nèi)容税课,通過自定義通知鈴聲的方式,達(dá)到語音播報(bào)提醒的目的痊剖。NSE方案也是蘋果在WWDC2019的Session707上推薦的解決方式韩玩。


消息推送流程
UNNotificationSound

在NSE中,可以通過給UNNotificationContent中的Sound屬性賦值來達(dá)到在通知彈出時(shí)播放一段自定義音頻的目的陆馁。

// The sound file to be played for the notification. The sound must be in the Library/Sounds folder of the app's data container or the Library/Sounds folder of an app group data container. If the file is not found in a container, the system will look in the app's bundle.
文檔中明確描述了音頻文件的存儲(chǔ)路徑找颓,以及讀取的優(yōu)先級(jí):

1.主應(yīng)用中的Library/Sounds文件夾中
2.AppGroups共享目錄中的Library/Sounds文件夾中
3.main bundle中

自定義鈴聲支持的聲音格式包括,aiff叮贩、wav以及caf格式击狮,鈴聲的長(zhǎng)度必須小于30s,否則系統(tǒng)會(huì)播放默認(rèn)的鈴聲益老。
鎖屏情況下彪蓬,鈴聲可以持續(xù)30s(與NES強(qiáng)制結(jié)束時(shí)間一致)在亮屏情況下鈴聲只能持續(xù)5s,是系統(tǒng)行為限制捺萌。
而且由于是通知鈴聲档冬,聲音是默認(rèn)跟靜音開關(guān)的。

AppGroups

由于我們是在NSE中自定義鈴聲桃纯,所以1和3這兩個(gè)文件路徑我們是無法訪問的酷誓。只能將合成好或者下載到語音音頻文件存儲(chǔ)到AppGroups下的Library/Sounds文件夾中,需要在Capablities中打開這個(gè)AppGroups的能力慈参,即可通過NSFileManager的containerURLForSecurityApplicationGroupIdentifier:方法訪問AppGroups的根目錄呛牲。

示例代碼:
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *containerUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:@"group.com.wchat.share"];
    
    NSString *newPath = [containerUrl.path stringByAppendingPathComponent:@"Library/Sounds"];
    // 清理本地緩存,刪除sounds文件夾(可選)
    [fileManager removeItemAtPath:newPath error:nil];
    
    if (![fileManager fileExistsAtPath:newPath]) {
        [fileManager createDirectoryAtPath:newPath withIntermediateDirectories:NO attributes:nil error:nil];
    }
    NSURL *localURL = [NSURL fileURLWithPath:[newPath stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@.%@", identifier, fileType]]];
    [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

    //設(shè)置播發(fā)音頻 文件名sname.sfileType
    self.bestAttemptContent.sound = [UNNotificationSound soundNamed:[NSString stringWithFormat:@"%@.%@", sname, sfileType]];

三驮配、開發(fā)過程中遇到的問題

消息播放隊(duì)列

NSE方案有個(gè)問題是:當(dāng)客戶端短時(shí)間內(nèi)收到多條播報(bào)通知時(shí)娘扩,后面的通知會(huì)頂?shù)羟懊娴耐ㄖ瑢?dǎo)致前面的通知播報(bào)不完整壮锻。所以需要增加一個(gè)消息隊(duì)列琐旁,將所有需要播報(bào)的通知都添加到隊(duì)列中,當(dāng)前面的消息播放完畢后猜绣,再播放后面的消息灰殴。

多線程問題

要注意的是,NSE的代碼邏輯并不是在主App工程中執(zhí)行的掰邢。一方面避免了開發(fā)者在NSE由于代碼設(shè)計(jì)失誤導(dǎo)致前臺(tái)的其他應(yīng)用界面卡住的問題牺陶,另一方面是主工程此時(shí)已被掛起或者已被kill掉伟阔,NSE本質(zhì)是push部分而不是調(diào)起主App。
所以我們?cè)谔幚砩厦嫣岬降南⒉シ抨?duì)列掰伸,以及涉及到文件讀寫的邏輯上皱炉,需要給相應(yīng)的代碼邏輯加鎖,否則會(huì)出現(xiàn)多線程問題狮鸭。

四合搅、NSE擴(kuò)展

iOS Notification Service Extension 共享空間數(shù)據(jù)互通測(cè)試

五、相關(guān)資料

Advances in App Background Execution - 2019
UNNotificationServiceExtension

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歧蕉,一起剝皮案震驚了整個(gè)濱河市灾部,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惯退,老刑警劉巖赌髓,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蒸痹,居然都是意外死亡春弥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門叠荠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扫责,你說我怎么就攤上這事榛鼎。” “怎么了鳖孤?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵者娱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我苏揣,道長(zhǎng)黄鳍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任平匈,我火速辦了婚禮框沟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘增炭。我一直安慰自己忍燥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布隙姿。 她就那樣靜靜地躺著梅垄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪输玷。 梳的紋絲不亂的頭發(fā)上队丝,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天靡馁,我揣著相機(jī)與錄音,去河邊找鬼机久。 笑死奈嘿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吞加。 我是一名探鬼主播裙犹,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼衔憨!你這毒婦竟也來了叶圃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤践图,失蹤者是張志新(化名)和其女友劉穎掺冠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體码党,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡德崭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揖盘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眉厨。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖兽狭,靈堂內(nèi)的尸體忽然破棺而出憾股,到底是詐尸還是另有隱情,我是刑警寧澤箕慧,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布服球,位于F島的核電站,受9級(jí)特大地震影響颠焦,放射性物質(zhì)發(fā)生泄漏斩熊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一伐庭、第九天 我趴在偏房一處隱蔽的房頂上張望粉渠。 院中可真熱鬧,春花似錦似忧、人聲如沸渣叛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淳衙。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箫攀,已是汗流浹背肠牲。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留靴跛,地道東北人缀雳。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梢睛,于是被迫代替她去往敵國和親肥印。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容