首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >符合Equatable for Diffing的协议

符合Equatable for Diffing的协议
EN

Stack Overflow用户
提问于 2019-10-27 21:50:54
回答 1查看 147关注 0票数 0

我这里有一个小聊天应用程序。

我可以有两种类型的消息:-文本-视频

我在解码JSON时使用了多态性,如下所示:

代码语言:javascript
复制
import Foundation

enum MessageType: Int, Decodable {
    case text
    case video
}

protocol Message: Decodable {
    static var type: MessageType { get }
    var id: String { get }
    var user: User { get}
    var timestamp: String { get }
}

struct TextMessage: Message {
    static var type: MessageType = .text
    var id: String
    var user: User
    var timestamp: String
    let text: String
}

struct VideoMessage: Message {
    static var type: MessageType = .video
    var id: String
    var user: User
    var timestamp: String
    let text: String
    let link: String
    let poster: String
}

enum MessageWrapper: Decodable {

    enum CodingKeys: String, CodingKey {
        case type
    }

    case text(TextMessage)
    case video(VideoMessage)

    var item: Message {
        switch self {
        case .text(let item): return item
        case .video(let item): return item
        }
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let type = try values.decode(Int.self, forKey: .type)
        switch type {
        case MessageType.text.rawValue: self = .text(try TextMessage(from: decoder))
        case MessageType.video.rawValue: self = .video(try VideoMessage(from: decoder))
        default:
            throw DecodingError.dataCorruptedError(forKey: .type,
                                                   in: values,
                                                   debugDescription: "Invalid type")
        }
    }
}

我还使用了MVVM方法,如下所示:

代码语言:javascript
复制
struct ChatViewModel {

    enum ViewModelType {
        case loading
        case text(TextMessageViewModel)
        case video(VideoMessageViewModel)
        case failure(ErrorViewModel)
    }

    enum State {
        case initialized
        case loading
        case loaded([Message])
        case failed(Error)
    }

    let state: State
    let viewModels: [ViewModelType]

    init(with state: State) {
        self.state = state
        switch state {
        case .initialized:
            viewModels = []
        case .loading:
            viewModels = [
                .loading,
            ]
        ......
    }
}

为了能够使用像Differ这样的Diffing库,ChatViewModel应该符合Equatable协议。

代码语言:javascript
复制
extension ChatViewModel: Equatable {
    static func == (lhs: ChatViewModel, rhs: ChatViewModel) -> Bool {
        return lhs.state == rhs.state
    }
}

extension ChatViewModel.State: Equatable {
    static func == (lhs: ChatViewModel.State, rhs: ChatViewModel.State) -> Bool {
        switch (lhs, rhs) {
            case (.initialized, .initialized): return true
            case (.loading, .loading): return true
            case let (.loaded(l), .loaded(r)): return l == r
            case let (.failed(l), .failed(r)): return l.localizedDescription == r.localizedDescription
            default: return false
        }
    }
}

这里的问题是对于case let (.loaded(l), .loaded(r)): return l == rMessage作为一种协议,并不符合Equatable

让它像这样符合Equatable

代码语言:javascript
复制
protocol Message: Decodable, Equatable {
    static var type: MessageType { get }
    var id: String { get }
    var user: User { get}
    var timestamp: String { get }
}

MessageWrapper生成错误Protocol 'Message' can only be used as a generic constraint because it has Self or associated type requirements

代码语言:javascript
复制
enum MessageWrapper: Decodable {

    ...

    var item: Message { // Protocol 'Message' can only be used as a generic constraint because it has Self or associated type requirements
        switch self {
        case .text(let item): return item
        case .video(let item): return item
        }
    }

    ...
}

有什么想法或建议来解决这个问题吗?我看到了一些关于Type Erasure的帖子,但经过一些测试后,我不确定它是否真的解决了问题。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-10-27 23:14:33

您不必为了能够使用==运算符而遵循Equatable。你可以自己定义一个这样的运算符,而不需要遵守Equatable

为了方便起见,我假设TextMessageVideoMessage已经符合Equatable

首先,编写一个比较Message的方法:

代码语言:javascript
复制
func messageEqual(m1: Message, m2: Message) -> Bool {
    if let textMessage1 = m1 as? TextMessage, let textMessage2 = m2 as? TextMessage {
        return textMessage1 == textMessage2
    }
    if let videoMessage1 = m1 as? VideoMessage, let videoMessage2 = m2 as? VideoMessage {
        return videoMessage1 == videoMessage2
    }
    return false
}

然后是[Message]==运算符

代码语言:javascript
复制
func ==(messages1: [Message], messages2: [Message]) -> Bool {
    return messages1.count == messages2.count && 
        zip(messages1, messages2).allSatisfy(messageEqual)
}

现在l == r应该可以编译了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58580011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档