diff --git a/Sources/XMTP/Codecs/AttachmentCodec.swift b/Sources/XMTP/Codecs/AttachmentCodec.swift index 4bb8e1ab..3c3ba5e8 100644 --- a/Sources/XMTP/Codecs/AttachmentCodec.swift +++ b/Sources/XMTP/Codecs/AttachmentCodec.swift @@ -55,4 +55,8 @@ public struct AttachmentCodec: ContentCodec { return attachment } + + public func fallback(content: Attachment) throws -> String? { + return "Can’t display “\(content.filename)”. This app doesn’t support attachments." + } } diff --git a/Sources/XMTP/Codecs/Composite.swift b/Sources/XMTP/Codecs/Composite.swift index 044d8654..ae5374f2 100644 --- a/Sources/XMTP/Codecs/Composite.swift +++ b/Sources/XMTP/Codecs/Composite.swift @@ -41,6 +41,10 @@ struct CompositeCodec: ContentCodec { let decodedComposite = fromComposite(composite: composite) return decodedComposite } + + public func fallback(content: DecodedComposite) throws -> String? { + return nil + } func toComposite(content decodedComposite: DecodedComposite) -> Composite { var composite = Composite() diff --git a/Sources/XMTP/Codecs/ContentCodec.swift b/Sources/XMTP/Codecs/ContentCodec.swift index 8036a5e2..c7ae44d7 100644 --- a/Sources/XMTP/Codecs/ContentCodec.swift +++ b/Sources/XMTP/Codecs/ContentCodec.swift @@ -71,6 +71,7 @@ public protocol ContentCodec: Hashable, Equatable { var contentType: ContentTypeID { get } func encode(content: T) throws -> EncodedContent func decode(content: EncodedContent) throws -> T + func fallback(content: T) throws -> String? } public extension ContentCodec { diff --git a/Sources/XMTP/Codecs/ReactionCodec.swift b/Sources/XMTP/Codecs/ReactionCodec.swift index 3a0b11e8..642c30d5 100644 --- a/Sources/XMTP/Codecs/ReactionCodec.swift +++ b/Sources/XMTP/Codecs/ReactionCodec.swift @@ -64,4 +64,13 @@ public struct ReactionCodec: ContentCodec { ) //swiftlint:disable force_unwrapping } + + public func fallback(content: Reaction) throws -> String? { + switch content.action { + case .added: + return "Reacted “\(content.content)” to an earlier message" + case .removed: + return "Removed “\(content.content)” from an earlier message" + } + } } diff --git a/Sources/XMTP/Codecs/ReadReceiptCodec.swift b/Sources/XMTP/Codecs/ReadReceiptCodec.swift index 36f81ba4..cbf39f49 100644 --- a/Sources/XMTP/Codecs/ReadReceiptCodec.swift +++ b/Sources/XMTP/Codecs/ReadReceiptCodec.swift @@ -41,4 +41,8 @@ public struct ReadReceiptCodec: ContentCodec { return ReadReceipt(timestamp: timestamp) } + + public func fallback(content: ReadReceipt) throws -> String? { + return nil + } } diff --git a/Sources/XMTP/Codecs/RemoteAttachmentCodec.swift b/Sources/XMTP/Codecs/RemoteAttachmentCodec.swift index 09eb394d..bc41c14c 100644 --- a/Sources/XMTP/Codecs/RemoteAttachmentCodec.swift +++ b/Sources/XMTP/Codecs/RemoteAttachmentCodec.swift @@ -132,6 +132,7 @@ public struct RemoteAttachment { } public struct RemoteAttachmentCodec: ContentCodec { + public typealias T = RemoteAttachment public init() {} @@ -189,6 +190,10 @@ public struct RemoteAttachmentCodec: ContentCodec { return attachment } + + public func fallback(content: RemoteAttachment) throws -> String? { + return "Can’t display “\(String(describing: content.filename))”. This app doesn’t support attachments." + } private func getHexParameter(_ name: String, from parameters: [String: String]) throws -> Data { guard let parameterHex = parameters[name] else { diff --git a/Sources/XMTP/Codecs/ReplyCodec.swift b/Sources/XMTP/Codecs/ReplyCodec.swift index 9d7197ce..754d3456 100644 --- a/Sources/XMTP/Codecs/ReplyCodec.swift +++ b/Sources/XMTP/Codecs/ReplyCodec.swift @@ -62,4 +62,8 @@ public struct ReplyCodec: ContentCodec { throw CodecError.invalidContent } } + + public func fallback(content: Reply) throws -> String? { + return "Replied with “\(content.content)” to an earlier message" + } } diff --git a/Sources/XMTP/Codecs/TextCodec.swift b/Sources/XMTP/Codecs/TextCodec.swift index 5e5f7495..54e29018 100644 --- a/Sources/XMTP/Codecs/TextCodec.swift +++ b/Sources/XMTP/Codecs/TextCodec.swift @@ -14,6 +14,7 @@ enum TextCodecError: Error { } public struct TextCodec: ContentCodec { + public typealias T = String public init() {} @@ -41,4 +42,8 @@ public struct TextCodec: ContentCodec { throw TextCodecError.unknownDecodingError } } + + public func fallback(content: String) throws -> String? { + return nil + } } diff --git a/Sources/XMTP/ConversationV1.swift b/Sources/XMTP/ConversationV1.swift index fce6cfa7..d30d766b 100644 --- a/Sources/XMTP/ConversationV1.swift +++ b/Sources/XMTP/ConversationV1.swift @@ -104,8 +104,19 @@ public struct ConversationV1 { let content = content as T var encoded = try encode(codec: codec, content: content) - encoded.fallback = options?.contentFallback ?? "" - + + func fallback(codec: Codec, content: Any) throws -> String? { + if let content = content as? Codec.T { + return try codec.fallback(content: content) + } else { + throw CodecError.invalidContent + } + } + + if let fallback = try fallback(codec: codec, content: content) { + encoded.fallback = fallback + } + if let compression = options?.compression { encoded = try encoded.compress(compression) } diff --git a/Sources/XMTP/ConversationV2.swift b/Sources/XMTP/ConversationV2.swift index 1910b06e..eba42062 100644 --- a/Sources/XMTP/ConversationV2.swift +++ b/Sources/XMTP/ConversationV2.swift @@ -98,9 +98,20 @@ public struct ConversationV2 { } var encoded = try encode(codec: codec, content: content) - encoded.fallback = options?.contentFallback ?? "" - - if let compression = options?.compression { + + func fallback(codec: Codec, content: Any) throws -> String? { + if let content = content as? Codec.T { + return try codec.fallback(content: content) + } else { + throw CodecError.invalidContent + } + } + + if let fallback = try fallback(codec: codec, content: content) { + encoded.fallback = fallback + } + + if let compression = options?.compression { encoded = try encoded.compress(compression) } diff --git a/Sources/XMTP/SendOptions.swift b/Sources/XMTP/SendOptions.swift index 49f8171c..15b09c0d 100644 --- a/Sources/XMTP/SendOptions.swift +++ b/Sources/XMTP/SendOptions.swift @@ -10,13 +10,11 @@ import Foundation public struct SendOptions { public var compression: EncodedContentCompression? public var contentType: ContentTypeID? - public var contentFallback: String? public var ephemeral: Bool = false - public init(compression: EncodedContentCompression? = nil, contentType: ContentTypeID? = nil, contentFallback: String? = nil, ephemeral: Bool = false) { + public init(compression: EncodedContentCompression? = nil, contentType: ContentTypeID? = nil, ephemeral: Bool = false) { self.compression = compression self.contentType = contentType - self.contentFallback = contentFallback self.ephemeral = ephemeral } } diff --git a/Tests/XMTPTests/CodecTests.swift b/Tests/XMTPTests/CodecTests.swift index 707f9e59..2165c0b4 100644 --- a/Tests/XMTPTests/CodecTests.swift +++ b/Tests/XMTPTests/CodecTests.swift @@ -9,6 +9,10 @@ import XCTest @testable import XMTP struct NumberCodec: ContentCodec { + func fallback(content: Double) throws -> String? { + return "pi" + } + typealias T = Double var contentType: XMTP.ContentTypeID { @@ -57,7 +61,7 @@ class CodecTests: XCTestCase { let aliceClient = fixtures.aliceClient! let aliceConversation = try await aliceClient.conversations.newConversation(with: fixtures.bob.address) - try await aliceConversation.send(content: 3.14, options: .init(contentType: NumberCodec().contentType, contentFallback: "pi")) + try await aliceConversation.send(content: 3.14, options: .init(contentType: NumberCodec().contentType)) // Remove number codec from registry Client.codecRegistry.codecs.removeValue(forKey: NumberCodec().id) diff --git a/Tests/XMTPTests/RemoteAttachmentTest.swift b/Tests/XMTPTests/RemoteAttachmentTest.swift index d58ff4f4..977202be 100644 --- a/Tests/XMTPTests/RemoteAttachmentTest.swift +++ b/Tests/XMTPTests/RemoteAttachmentTest.swift @@ -41,7 +41,7 @@ class RemoteAttachmentTests: XCTestCase { remoteAttachmentContent.filename = "hello.txt" remoteAttachmentContent.contentLength = 5 - _ = try await conversation.send(content: remoteAttachmentContent, options: .init(contentType: ContentTypeRemoteAttachment, contentFallback: "hey")) + _ = try await conversation.send(content: remoteAttachmentContent, options: .init(contentType: ContentTypeRemoteAttachment)) } func testCanUseAttachmentCodec() async throws { diff --git a/XMTP.podspec b/XMTP.podspec index c8d30727..0255b1e2 100644 --- a/XMTP.podspec +++ b/XMTP.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "XMTP" - spec.version = "0.5.2-alpha0" + spec.version = "0.5.3-alpha0" spec.summary = "XMTP SDK Cocoapod" # This description is used to generate tags and improve search results.