ローカル通知の音を動的に変更する(iOS10以降)

4 min読了の目安(約2700字TECH技術記事

ローカル通知の着信音をカスタマイズしたいんだけど、っていうニーズはまさにFAQで、stackoverflowなんかにもたくさんあがっています。

Choose custom sound for local notifications

iOS9以前では、UILocalNotificationsoundNameプロパティにメインバンドルに含まれる.cafファイルの名前を指定すればその音を鳴らせるようになっていました。逆に言うと、あらかじめバンドルに入れてビルドした音声ファイルしか通知を受信したときに鳴らすことが出来ませんでした。

iOS10ではUILocalNotificationはdeprecatedとなり、新たにUNNotificationRequestUNNotificationContentを使って通知を送ることが推奨されるようになりました。

概要は下記の記事などを参照下さい。

iOS 10以降のNotificationの基本

UNNotificationContentには、UNNotificationSound型のsoundというプロパティが追加されています。

このUNNotificationSoundの、init(named:)コンストラクタのnamedパラメータの説明に、

The name of the sound file to play. This file must be located in the current executable’s main bundle or in the 
Library/Sounds directory of the current app container directory. This parameter must not be nil.

とあり、iOS10からは従来のメインバンドルだけでなく、Library/Soundsの下に置いたファイル名を指定できるようになりました。

レアケースなんですけど、インハウスの業務用向けアプリの開発で、特殊な環境の現場で使うので、どんな通知音が聞き取りやすいかは現場で使ってもらわないと選べないから、通知の音はシステムの効果音のなかから、後で任意に選べるようにして欲しいというオーダーがありました。

システム効果音は、 /System/Library/Audio/UISoundsなどの中に納められているので、ローカル通知を送る前にこれらのファイルを選んで、<App>/Library/Soundsにコピーしておくことで、システム効果音をならすことが出来ました。

通常、アプリケーションのLibraryの下にSoundsディレクトリはないので、作成します。

参考:File System Basics:About the iOS File System

// <App>/Library/Soundsが無ければ作成
let libraryUrl = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
let soundDirUrl = libraryUrl.appendingPathComponent("Sounds")
try? FileManager.default.createDirectory(at: soundDirUrl, withIntermediateDirectories: true, attributes: nil)

Library/Soundsの下に効果音ファイルを置きます。


// あらかじめリストアップしておいたファイルから選んで${App}/Library/Soundsにコピー
do {
        let from =  URL(fileURLWithPath:"/System/Library/Audio/UISounds/Modern").appendingPathComponent("calendar_alert_chord.caf")
        let dest = soundDirUrl.appendingPathComponent(file)
        try FileManager.default.copyItem(at: from, to: dest)
}catch{
        log.error(error)
        return false
}
return true

UNNotificationSoundに先ほどコピーした"calendar_alert_chord.caf"を指定して、ローカル通知を送ります。


 let content = UNMutableNotificationContent()
 content.title = ".."
 content.subtitle = "..."
 content.body = ".."
 content.userInfo = [...]

 content.sound = UNNotificationSound(named:"calendar_alert_chord.caf")

 let request = UNNotificationRequest.init(identifier: UUID().uuidString, content: content, trigger: nil)

 let center = UNUserNotificationCenter.current()
 center.add(request)

以上です。