Dan*_*ini 5 ios swift swiftui combine
我正在尝试 SwiftUI,并在为我的一个列表实现数据模型时遇到了问题。我的计划是创建一个协议CardProtocol作为列表元素的数据协议,然后使用该协议的 CoreData 实现以及用于单元测试和 Canvas 使用的虚拟协议。如果您在 SwiftUI 中使用数据集合,List则单个元素需要符合Identifiable协议。
代码如下所示:
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 }
}
Run Code Online (Sandbox Code Playgroud)
这甚至无法编译,因为Identifiable协议有 2 个关联类型,如果要将协议用于变量定义,则需要指定这两个类型。
/// 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 }
}
Run Code Online (Sandbox Code Playgroud)
确切的错误是error: protocol 'CardProtocol' can only be used as a generic constraint because it has Self or associated type requirements. 现在ID不是问题并且可以修复,但IdentifiedValue它在 CoreData 和虚拟实现中本质上是不同的。
我发现的唯一合理的解决方案是Identifiable从协议中删除对协议的遵从性,并稍后在视图中使用 重新引入它cardsModel.cards.identified(by: \.id)。有没有更好的方法来解决这个问题,让我在协议级别保持可识别的合规性?
除了通过 添加 Identificate 之外.identified(by: \.id),唯一的解决方案是使用类型擦除模式。这使用 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 {}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1992 次 |
| 最近记录: |