求助:IntentHandler类ID与Contact类fromId返回ID不一致问题
问题:IntentHandler使用的ID与Contact.fromId获取的ID不一致
问题背景
正在排查IntentHandler类中使用的ID与Contact类fromId方法获取的ID不一致的问题,尝试多种方法仍未定位原因,涉及项目为"Dynamic Intent Widget"。
相关代码
Contact类
struct Contact: Identifiable, Hashable, Codable { var id = UUID().uuidString var name: String var dateOfBirth: Date } extension Contact { static func getAll() -> [Contact] { let key = Key.contacts.rawValue guard let contacts: [Contact] = UserDefaults.appGroup.getArray(forKey: key) else { let contacts: [Contact] = [.friend1, .friend2] UserDefaults.appGroup.setArray(contacts, forKey: key) return contacts } return contacts } static let friend1: Contact = { let date = Calendar.current.date(byAdding: .month, value: -2, to: Date())! return Contact(name: "Friend 1", dateOfBirth: date) }() static let friend2: Contact = { let date = Calendar.current.date(byAdding: .year, value: -3, to: Date())! return Contact(name: "Friend 2", dateOfBirth: date) }() } extension Contact { static func fromId(_ id: String) -> Contact? { getAll().first { $0.id == id } } }
UserDefaults扩展
extension UserDefaults { func setArray<Element>(_ array: [Element], forKey key: String) where Element: Encodable { let data = try? JSONEncoder().encode(array) set(data, forKey: key) } func getArray<Element>(forKey key: String) -> [Element]? where Element: Decodable { guard let data = data(forKey: key) else { return nil } return try? JSONDecoder().decode([Element].self, from: data) } }
IntentHandler类
import Intents class IntentHandler: INExtension, DynamicPersonSelectionIntentHandling { func providePersonOptionsCollection( for intent: DynamicPersonSelectionIntent, with completion: @escaping (INObjectCollection<Person>?, Error?) -> Void ) { let persons = Contact.getAll() .map { Person(identifier: $0.id, display: $0.name) } let collection = INObjectCollection(items: persons) completion(collection, nil) } }
问题原因分析
核心问题出在Contact结构体的id属性定义上:
id使用了默认值生成:var id = UUID().uuidString,每次创建Contact实例(包括通过JSONDecoder解码时),都会生成一个新的UUID字符串。- 首次调用
getAll()时,生成的friend1和friend2会被存入UserDefaults,此时它们的id是初始生成的UUID。但后续再次调用getAll()从UserDefaults读取时,JSONDecoder会重新创建Contact实例,id会被重新赋值为新的UUID,导致存储的id和读取后的id不一致。 - IntentHandler中使用的是某次读取的Contact的id,而后续调用
fromId时,读取的Contact的id已经是新生成的,自然无法匹配,返回nil。
解决方案
1. 修改Contact的id属性定义
将id改为不可变属性(let),并移除默认值,改为初始化时必须传入:
struct Contact: Identifiable, Hashable, Codable { let id: String // 移除默认值,改为初始化传入 var name: String var dateOfBirth: Date }
2. 调整静态实例的初始化
在创建friend1和friend2时,手动传入UUID:
static let friend1: Contact = { let date = Calendar.current.date(byAdding: .month, value: -2, to: Date())! return Contact(id: UUID().uuidString, name: "Friend 1", dateOfBirth: date) }() static let friend2: Contact = { let date = Calendar.current.date(byAdding: .year, value: -3, to: Date())! return Contact(id: UUID().uuidString, name: "Friend 2", dateOfBirth: date) }()
3. 确保解码时正确解析id
修改后id是Codable的一部分,JSONEncoder会将其编码进数据,JSONDecoder解码时会正确读取原来的id,不会重新生成,这样每次从UserDefaults读取的Contact的id都会和存储时一致。
这样修改后,IntentHandler传递的id和fromId查询时使用的id就会保持一致,问题即可解决。
内容的提问来源于stack exchange,提问作者PrimeCookie
相关产品推荐
相关产品推荐

