【问题标题】:Generic parameter 'Subject' could not be inferred无法推断通用参数“主题”
【发布时间】:2025-11-25 20:00:02
【问题描述】:

背景

我有一个核心数据实体 Entry 与另一个实体 DataField 有关系:

class Entry: NSManagedObject {
    //[...]
    @NSManaged public var stringFields: NSSet?
}

因为在大多数情况下使用NSSet? 是不切实际的,所以Entry 有另一个变量:

var dataFields: [DataField] {     
    let arr = stringFields?.allObjects as! [DataField]
    return arr.sorted(by: { $0.order < $1.order })
}

DataField 看起来像这样:

class DataField: NSManagedObject {
    //[...]
    @NSManaged var value: String
}    

EntryStore 是我对整个事情的包装:

class EntryStore: NSObject, BindableObject {
    private lazy var fetchedResultsController: NSFetchedResultsController<Entry> = {
        //[...]
        fetchedResultsController.delegate = self
        return fetchedResultsController
    }()
    var entries: [Entry] {
        return fetchedResultsController.fetchedObjects ?? []
    }
    let didChange = PassthroughSubject<EntryStore, Never>()
    //[...]
}
extension EntryStore: NSFetchedResultsControllerDelegate {
    public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        didChange.send(self)
    }
}

最后,我在 SceneDelegate 中将 EntryStore 作为 @EnvironmentObject 传递

问题

虽然我可以将 Entity 的其他属性与 SwiftUI 文本字段一起使用,但当我尝试使用 DataField 时,会出现此错误:

无法推断通用参数“主题”

代码:

struct ContentView: View {
    @EnvironmentObject var model: EntryStore
    var body: some View {
        TextField($model.entries.first!.dataFields.first!.value)
    }
}

我读过这个错误的一般形式是没有明确输入变量的结果,但是我尝试过强制转换这个表达式的各种方法都无济于事。此外,对于它的价值,当我将 dataFields 转换内联移动到 TextField 时,Xcode 中的红线似乎位于 .allObjects 部分:

TextField(($model.stringFields.allObjects.sort(by: { ($0 as! DataField).order < ($1 as! DataField).order }).first!.value)

关于如何解决这个问题的任何想法?

【问题讨论】:

  • 在你的最后一个例子中,你需要一个 ?在stringFields 之后,因为它是可选的。我想这就是你得到红线的原因
  • 我最初也是这样,但由于某种原因添加了 ?实际上产生了这个错误:Cannot use optional chaining on non-optional value of type 'Binding&lt;NSSet?&gt;
  • 好的,绑定似乎是一个新功能,也是我还不熟悉的东西。

标签: swift generics core-data swiftui


【解决方案1】:

问题在于dataFields 的值更改时没有通知PasstrhoughSubject - 这意味着它不能用作绑定。

这是我最终得到的结果(beta 4 也有细微的变化)- 首先给 Entry 一个发布者:

public class Entry: NSManagedObject, BindableObject {
    public let willChange = PassthroughSubject<Entry, Never>()

然后使用setter和getter来提醒Combine框架的变化:

var dataFields: [DataField] {
    set {
        willChange.send(self)
    }
    get {
        let arr = stringFields?.allObjects as! [DataField]
        return arr.sorted(by: { $0.order < $1.order })
    }
}

}

现在dataFields 可以与绑定一起使用

【讨论】: