gpt4 book ai didi

ios - SwiftUI 列表数据的可识别协议(protocol)扩展

转载 作者:搜寻专家 更新时间:2023-10-31 21:49:09 25 4
gpt4 key购买 nike

我正在试验 SwiftUI,在为我的一个列表实现数据模型时遇到了一个问题。我的计划是创建一个协议(protocol) CardProtocol 作为我列表元素的数据协议(protocol),然后有一个 CoreData 协议(protocol)实现以及一个用于单元测试和 Canvas 使用的虚拟协议(protocol)。如果您在 SwiftUI List 中使用数据集合,则单个元素需要符合 Identifiable 协议(protocol)。

代码如下所示:

import SwiftUI
import Combine


final class CardsModel: BindableObject {
var cards: [CardProtocol] = []
let didChange = PassthroughSubject<CardsModel, Never>()
}

protocol CardProtocol: Identifiable {
var id: Int { get set }
var firstName: String? { get set }
var lastName: String? { get set }
var email: String? { get set }
var phone: String? { get set }
}

这甚至无法编译,因为 Identifiable 协议(protocol)有 2 个关联类型,如果要将协议(protocol)用于变量定义,则需要指定这些类型。

/// A type that can be compared for identity equality.
public protocol Identifiable {

/// A type of unique identifier that can be compared for equality.
associatedtype ID : Hashable

/// A unique identifier that can be compared for equality.
var id: Self.ID { get }

/// The type of value identified by `id`.
associatedtype IdentifiedValue = Self

/// The value identified by `id`.
///
/// By default this returns `self`.
var identifiedValue: Self.IdentifiedValue { get }
}

确切的错误是error: protocol 'CardProtocol' can only be used as a generic constraint because it has Self or associated type requirements。现在 ID 不是问题,可以修复,但是 IdentifiedValue 在 CoreData 和虚拟实现中本质上是不同的。

我找到的唯一合理的解决方案是从协议(protocol)中删除对 Identifiable 的遵从性,并稍后在 View 中使用 cardsModel.cards.identified(by:\.id)< 重新引入它。有没有更好的方法可以让我在协议(protocol)级别保持可识别的合规性?

最佳答案

除了通过 .identified(by:\.id) 添加 Identifiable 之外,唯一的解决方案是使用类型删除模式。这使用 3 个类来装箱和隐藏关联类型,然后允许声明一个 AnyCard 对象数组。该实现非常庞大,对于我的问题来说可能不值得。但它是这样的:

final class CardsModel<IdentifiedValue:CardProtocol>: BindableObject {
var cards: [AnyCard<IdentifiedValue>] = []
let didChange = PassthroughSubject<CardsModel, Never>()
}

protocol CardProtocol: Identifiable{
var id: Int32 { get set }
var firstName: String? { get set }
var lastName: String? { get set }
var email: String? { get set }
var phone: String? { get set }
}

struct TestCard: CardProtocol {
var id: Int32
var firstName: String?
var lastName: String?
var email: String?
var phone: String?
}

extension CardsModel where IdentifiedValue == TestCard {
convenience init(cards: [TestCard]) {
self.init()
self.cards = cards.map({ (card) -> AnyCard<TestCard> in
return AnyCard(card)
})
}
}

private class _AnyCardBase<IdentifiedValue>: CardProtocol {
init() {
guard type(of: self) != _AnyCardBase.self else {
fatalError("_AnyCardBase<Model> instances can not be created; create a subclass instance instead")
}
}

var id: Int32 {
get { fatalError("Must override") }
set { fatalError("Must override") }
}

var firstName: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}

var lastName: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}

var email: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}

var phone: String? {
get { fatalError("Must override") }
set { fatalError("Must override") }
}
}


private final class _AnyCardBox<Concrete: CardProtocol>: _AnyCardBase<Concrete.IdentifiedValue> {
var concrete: Concrete

init(_ concrete: Concrete) {
self.concrete = concrete
}

override var id: Int32 {
get {
return concrete.id
}
set {
concrete.id = newValue
}
}

override var firstName: String? {
get {
return concrete.firstName
}
set {
concrete.firstName = newValue
}
}

override var lastName: String? {
get {
return concrete.lastName
}
set {
concrete.lastName = newValue
}
}

override var email: String? {
get {
return concrete.email
}
set {
concrete.email = newValue
}
}

override var phone: String? {
get {
return concrete.phone
}
set {
concrete.phone = newValue
}
}
}

final class AnyCard<IdentifiedValue>: CardProtocol {
private let box: _AnyCardBase<IdentifiedValue>

init<Concrete: CardProtocol>(_ concrete: Concrete) where Concrete.IdentifiedValue == IdentifiedValue {
box = _AnyCardBox(concrete)
}

var id: Int32 {
get {
return box.id
}
set {
box.id = newValue
}
}

var firstName: String? {
get {
return box.firstName
}
set {
box.firstName = newValue
}
}

var lastName: String? {
get {
return box.lastName
}
set {
box.lastName = newValue
}
}

var email: String? {
get {
return box.email
}
set {
box.email = newValue
}
}

var phone: String? {
get {
return box.phone
}
set {
box.phone = newValue
}
}
}

//NSManagedObject extention
extension Card:CardProtocol {}

关于ios - SwiftUI 列表数据的可识别协议(protocol)扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56552993/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com