From f019b0bc773dac31e4f0264b2fa60f2bdcb8767d Mon Sep 17 00:00:00 2001 From: Thomas Rademaker Date: Fri, 27 Mar 2026 12:36:51 -0400 Subject: [PATCH] support failable decodable for chat --- Sources/bskyKit/Models/Chat.swift | 44 +++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/Sources/bskyKit/Models/Chat.swift b/Sources/bskyKit/Models/Chat.swift index 0e7cfca..d4a26ef 100644 --- a/Sources/bskyKit/Models/Chat.swift +++ b/Sources/bskyKit/Models/Chat.swift @@ -1,9 +1,29 @@ import Foundation +// MARK: - Fault-Tolerant Decoding + +private struct FailableDecodable: Decodable, Sendable { + let value: T? + init(from decoder: Decoder) throws { + value = try? T(from: decoder) + } +} + /// Response from chat.bsky.convo.listConvos public struct ConvoListResponse: Codable, Sendable { public let convos: [ConvoView] public let cursor: String? + + enum CodingKeys: String, CodingKey { + case convos, cursor + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + cursor = try container.decodeIfPresent(String.self, forKey: .cursor) + let failableConvos = try container.decode([FailableDecodable].self, forKey: .convos) + convos = failableConvos.compactMap(\.value) + } } /// A conversation view @@ -14,6 +34,7 @@ public struct ConvoView: Codable, Sendable, Identifiable { public let lastMessage: ChatMessageUnion? public let muted: Bool public let unreadCount: Int + public let status: String? } /// Union type for messages @@ -32,9 +53,17 @@ public enum ChatMessageUnion: Codable, Sendable { switch type { case "chat.bsky.convo.defs#messageView": - self = .message(try ChatMessageView(from: decoder)) + if let message = try? ChatMessageView(from: decoder) { + self = .message(message) + } else { + self = .unknown + } case "chat.bsky.convo.defs#deletedMessageView": - self = .deleted(try DeletedMessageView(from: decoder)) + if let deleted = try? DeletedMessageView(from: decoder) { + self = .deleted(deleted) + } else { + self = .unknown + } default: self = .unknown } @@ -93,6 +122,17 @@ public struct ChatMember: Codable, Sendable, Identifiable { public struct ChatMessagesResponse: Codable, Sendable { public let messages: [ChatMessageUnion] public let cursor: String? + + enum CodingKeys: String, CodingKey { + case messages, cursor + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + cursor = try container.decodeIfPresent(String.self, forKey: .cursor) + let failableMessages = try container.decode([FailableDecodable].self, forKey: .messages) + messages = failableMessages.compactMap(\.value) + } } /// Response from chat.bsky.convo.sendMessage -- 2.51.2