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

求助: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:24:20