You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CallKit Swift动态手机号数组拦截问题:静态生效动态添加无效

来电拦截App动态添加手机号无法生效的解决方案

问题背景

我正在开发一款支持手机号拦截功能的iOS App,使用CallDirectory扩展实现拦截逻辑。最初使用静态手机号数组时功能正常,但通过TextField动态添加手机号到数组后,拦截功能完全失效。先后尝试用ObservableObject管理数组、将手机号存储到UserDefaults,问题都未解决。

初始版本代码

CallDirectory扩展代码

class CallDirectoryHandler: CXCallDirectoryProvider, CXCallDirectoryExtensionContextDelegate {
    func requestFailed(for extensionContext: CXCallDirectoryExtensionContext, withError error: Error) {
        print("Errorrrrrrrrr",error)
    }
    override func beginRequest(with context: CXCallDirectoryExtensionContext) {
        context.delegate = self
//        let numList = NumberList()
        var numList = NumberList()
        for num in numList.array{
            let pNum :  CXCallDirectoryPhoneNumber = CXCallDirectoryPhoneNumber(Int64(num)!)
            context.addBlockingEntry(withNextSequentialPhoneNumber: pNum)

        }
        context.completeRequest()
    }
}

手机号数组管理类

class NumberList : ObservableObject{
    @Published var array:[String] = []
    
    //more code will goes there
}

View代码

struct AddNumber: View {
    @StateObject var numberList = NumberList()
    @State var number = ""
    var body: some View {
        VStack{
            HStack{
                TextField("Enter Number", text: $number)
                    .padding()
            }
            .frame(width: Constants.width*0.7, height: Constants.height*0.07)
            .background(Color("Button"))
            .cornerRadius(30)
            Button {
                numberList.array.append(number)
            } label: {
                Text("Save")
                    .foregroundColor(.white)
            }
            Button{
                CXCallDirectoryManager.sharedInstance.reloadExtension(withIdentifier: "XYZ.com.BlockContacts.CallBlocker") { (error) in
                    print("reloaded extension: \(String(describing: error))")
                }
            } label: {
                Text("Block")
                    .font(.custom("Montserrat-Regular", size: 24))
                    .frame(width: Constants.width*0.6, height: Constants.height*0.06)
                    .background(Color.red)
                    .foregroundColor(.white)
                    .cornerRadius(30)
            }
        }
    }
}

更新后的代码

Save按钮代码

Button {
                        if var data = UserDefaults.standard.array(forKey: "phoneNumber") as? [String]{
                            data.append(number)
                            UserDefaults.standard.set(data,forKey: "phoneNumber")
                        }else{
                            let ar = [number]
                            UserDefaults.standard.set(ar,forKey: "phoneNumber")
                        }
                    } label: {
                        Text("Save")
                            .foregroundColor(.white)
                    }

更新后的CallDirectoryHandler.swift

class CallDirectoryHandler: CXCallDirectoryProvider, CXCallDirectoryExtensionContextDelegate {
    func requestFailed(for extensionContext: CXCallDirectoryExtensionContext, withError error: Error) {
        print("Errorrrrrrrrr",error)
    }
    override func beginRequest(with context: CXCallDirectoryExtensionContext) {
        context.delegate = self
        let data = UserDefaults.standard.array(forKey: "phoneNumber") as? [String]
        print(data)
        for num in data ?? []{
            let pNum :  CXCallDirectoryPhoneNumber = CXCallDirectoryPhoneNumber(Int64(num)!)
            context.addBlockingEntry(withNextSequentialPhoneNumber: pNum)

        }
        context.completeRequest()
    }
}

问题分析

  1. 初始版本核心问题:CallDirectory扩展是独立于主App的进程,和主App不共享内存。扩展中新建的NumberList实例是全新的空数组,根本无法获取主App中添加的数据,ObservableObject仅能在单个进程内共享状态,跨进程完全无效。

  2. UserDefaults版本核心问题:主App和CallDirectory扩展默认使用各自的UserDefaults容器,彼此隔离。你没有配置App Groups,导致扩展无法读取主App存储的数据。

另外还有一个隐藏问题:CallDirectory要求拦截的号码必须是E.164标准格式(带国家码前缀,如+8613800138000),直接将无+号的号码转Int64会导致格式错误,无法被系统识别。

解决方案

1. 配置App Groups

  • 打开Xcode,选中主App Target,进入Signing & Capabilities,点击**+ Capability添加App Groups**。
  • 创建一个格式为group.com.yourcompany.yourapp的App Group(例如group.com.XYZ.BlockContacts)。
  • 给CallDirectory扩展Target添加相同的App Groups,确保两者加入同一个组。

2. 修改UserDefaults为共享容器

主App和扩展都要使用App Group对应的共享UserDefaults,而非默认的standard容器。

主App Save按钮代码修改

Button {
    // 替换为你的App Group ID
    guard let sharedDefaults = UserDefaults(suiteName: "group.com.XYZ.BlockContacts") else {
        print("无法初始化共享UserDefaults")
        return
    }
    
    var phoneNumbers = sharedDefaults.array(forKey: "phoneNumber") as? [String] ?? []
    phoneNumbers.append(number)
    sharedDefaults.set(phoneNumbers, forKey: "phoneNumber")
    sharedDefaults.synchronize() // 确保数据立即写入
} label: {
    Text("Save")
        .foregroundColor(.white)
}

CallDirectory扩展代码修改

class CallDirectoryHandler: CXCallDirectoryProvider, CXCallDirectoryExtensionContextDelegate {
    func requestFailed(for extensionContext: CXCallDirectoryExtensionContext, withError error: Error) {
        print("Errorrrrrrrrr", error)
    }
    
    override func beginRequest(with context: CXCallDirectoryExtensionContext) {
        context.delegate = self
        // 替换为你的App Group ID
        guard let sharedDefaults = UserDefaults(suiteName: "group.com.XYZ.BlockContacts") else {
            print("无法访问共享UserDefaults")
            context.completeRequest()
            return
        }
        
        let phoneNumbers = sharedDefaults.array(forKey: "phoneNumber") as? [String] ?? []
        print("读取到的拦截号码:", phoneNumbers)
        
        for num in phoneNumbers {
            // 格式化号码为E.164标准格式(这里以中国号码为例,补全+86前缀)
            let formattedNumber = num.hasPrefix("+") ? num : "+86\(num)"
            // 安全转换为CXCallDirectoryPhoneNumber
            guard let phoneNum = CXCallDirectoryPhoneNumber(formattedNumber) else {
                print("无效号码:\(num),跳过")
                continue
            }
            context.addBlockingEntry(withNextSequentialPhoneNumber: phoneNum)
        }
        
        context.completeRequest()
    }
}

3. 额外检查事项

  • 确保用户已在系统设置中给App开启电话权限,并允许来电拦截与识别。
  • 测试时输入符合E.164格式的号码,或确保代码中正确处理号码前缀。
  • 调试扩展时,可在Xcode中选择CallDirectory扩展Target运行,查看控制台日志确认数据读取情况。

内容的提问来源于stack exchange,提问作者Iftikhar Hussain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:07:01