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() } }
问题分析
初始版本核心问题:CallDirectory扩展是独立于主App的进程,和主App不共享内存。扩展中新建的
NumberList实例是全新的空数组,根本无法获取主App中添加的数据,ObservableObject仅能在单个进程内共享状态,跨进程完全无效。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
相关产品推荐
相关产品推荐

