iOS 簡訊過濾筆記|Message Filter App Extension

前陣子在研究 iOS 的簡訊過濾功能,想說是不是也能做出像 Whoscall 那樣能幫忙擋垃圾簡訊的東西。結果研究下來才發現,表面看似簡單,實際上被蘋果設了不少限制😂

這篇就當我的研究筆記,順便記錄一些實作心得,給未來的自己和有興趣研究這功能的開發者參考。
那就開始吧!

Message Filter 是什麼?

Message Filter App Extension 流程圖

Message Filter App Extension 是蘋果在 iOS 11 之後提供的 API,讓開發者可以協助系統過濾「簡訊」。但這裡的「簡訊」只限於 SMS,不是 iMessage。簡單說,就是幫系統判斷這封訊息要不要歸類成垃圾或促銷。

系統分類機制

如果你有看過「未知與垃圾訊息」的分頁,那就是這個機制在運作。當簡訊進來時,系統會詢問 Extension 該如何處理,依照回傳的分類決定是否要顯示在主要收件匣中。

開發前的準備

建立主 App 與 Extension

首先要知道,Message Filter 不能單獨存在。它必須掛在一個主 App 底下,所以得先建一個主 App 專案,再加上 Message Filter Extension。

共用資料與設定

App 和 Extension 要共用資料,必須設定 App Group。這樣才能共用像 Realm 資料庫或設定檔等內容。

如果還想讓它連線到後端 API(例如把未知簡訊送去伺服器判斷),就要設定 Shared Web Credentials 與 apple-app-site-association 檔案,這個檔案要放在後端的根目錄或 .well-known 資料夾裡。

iOS 14+ 取得 AASA 的變更與快取

  • CDN 快取:自 iOS 14 / macOS 11 起,裝置不再直接向你的主機抓取 AASA 檔案,而是透過 Apple 的 CDN 取得並快取。
  • Developer alternate mode:開發調試時若想繞過 CDN 直接連你的主機,可在 Associated Domains entitlement 中於網域後加上查詢參數 ?mode=developer,例如:
Bash
applinks:example.com?mode=developer webcredentials:example.com?mode=developer
  • 這能讓裝置直接抓取你伺服器上的最新版 AASA,降低等待 CDN 更新的時間。完成上線前,請移除該參數,避免非預期行為。

簡訊過濾的原理

本地端分類

簡訊收到後,系統會先詢問 Extension:這封要分類成什麼?

Swift
func handle(_ queryRequest: ILMessageFilterQueryRequest,
            context: ILMessageFilterExtensionContext,
            completion: @escaping (ILMessageFilterQueryResponse) -> Void) {
    let response = ILMessageFilterQueryResponse()
    response.action = .junk // 或 .promotion、.transaction 等
    completion(response)
}

這段就是最基本的邏輯:決定訊息的去向。

後端分類

若想交給後端判斷,可用 deferQueryRequestToNetwork() 讓系統自行呼叫 API。但要注意:

  • API 的 URL 必須寫死在 Info.plist 中,不能動態變更。
Bash
<key>NSExtension</key>
    <dict>
        <key>NSExtensionPrincipalClass</key>
            <string>MessageFilterExtension</string>
        <key>NSExtensionAttributes</key>
            <dict>
                <key>ILMessageFilterExtensionNetworkURL</key>
                <string>https://www.example-sms-filter-application.com/api</string>
            </dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.identitylookup.message-filter</string>
     </dict>
  • 請求格式固定,開發者無法自訂。
Bash
POST /server-endpoint HTTP/1.1
Accept: */*
Content-Type: application/json; charset=utf-8
Content-Length: 148
{
    "_version": 1,
    "query": {
        "sender": "14085550001",
        "message": {
            "text": "This is a message"
        }
    },
    "app": {
        "version": "1.1"
    }
}

參考資料:Creating a Message Filter App Extension

能過濾什麼?

其實這個功能限制很多:

資料取得限制

  1. 只能處理 未知聯絡人 的 SMS,iMessage 無法過濾。
  2. 無法讀取舊簡訊,只能處理「當下收到」的內容。

可用分類

可用分類是系統定義好的:

  • transaction(交易)
  • promotion(促銷)
  • junk(垃圾)

自 iOS 16 起,蘋果在原本三大分類下(transaction、promotion、junk)新增了更多子分類,總共有 12 個:

  • 交易 (transaction):財務 (finance)、訂單 (orders)、提醒 (reminders)、健康 (health)、公共服務 (public services)
  • 促銷 (promotion):天氣 (weather)、電信營運商 (carrier)、獎勵 (rewards)、其他促銷內容 (other promotions)
  • 垃圾 (junk):優惠券 (coupons)、優惠 (offers)、其他垃圾內容 (other junk)

這些子分類能讓訊息分類更精細,但開發者仍無法自訂新的分類或修改系統既有分類。

延伸閱讀

iOS 16 之後的更新重點

  • 子分類(Subcategories):除了三大類外,可進一步標註至細部子分類,方便使用者在「未知與垃圾訊息」頁面瀏覽與檢索。實作上,子分類屬於官方定義的列舉(IdentityLookup.framework),需回傳系統支援的值。
  • 能力查詢(Capabilities Query):在 iOS 16 後,系統會透過 ILMessageFilterCapabilitiesQueryRequest 詢問 Extension 支援哪些分類/子分類與行為,你需要在對應的 handle(capabilitiesQueryRequest:...) 中回報(例如宣告支援網路過濾、支援的分類集合)。
  • 文件與範例:建議搭配 WWDC22〈Explore SMS message filters〉與最新的 IdentityLookup 文件核對子分類常數與可用行為,避免使用到已廢棄或區域限定的值。

補充:部份分類與子分類在特定市場才會預設出現;請以官方最新文件為準,並以實機在目標地區版本測試。

使用者啟用設定

就算 Extension 寫好了,用戶也要手動開啟這功能:

路徑: 設定 → 訊息 → 未知與垃圾訊息 → SMS 過濾 → 選擇你的 App。

無法直接導向設定頁

若想從 App 跳轉,蘋果未提供直接 API。只能開啟 App 自身設定頁:

Swift
if let appSettings = URL(string: UIApplication.openSettingsURLString) {
    UIApplication.shared.open(appSettings)
}

這只能帶使用者到 App 的設定頁,無法直接開到訊息設定。

審核與上架注意事項

Bundle 與 Target 設定

帶有 Extension 的 App 是兩個 Target,需確保 Bundle ID 前綴一致。App Store Connect 新增 App 時只需主 App 的 ID。

上架測試

上架前一定要測試主 App 與 Extension 是否正常,並符合蘋果的安全標準,否則容易被退件。

參考資料:【iOS 開發】帶有 Extension Target 的 App,如何簽名打包

實際使用心得

這功能適合用於研究與實驗,不太適合商業防詐應用。限制太多、權限太嚴格,也無法存取歷史訊息。蘋果明顯不希望開發者攔截太多使用者資料。

從另一個角度看,這也體現了蘋果對隱私與安全的高標準。若任何 App 都能讀簡訊內容,後果會非常嚴重。


這次研究 Message Filter App Extension 算是一次有趣的挖坑經驗。過程中不斷發現「欸?原來這不能做」、「這又被鎖起來」😂

不過至少對整個系統機制有更深的了解,感覺如果真的想做防詐功能,還是得靠後端配合,而不是全靠 iOS 端。

分享這篇文章

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *