Build custom content types for use with XMTP
Your custom content type WILL NOT automatically be supported by other apps and will display fallback text in them instead.
Building a custom content type enables you to manage data in a way that is more personalized or specialized to the needs of your app.
For more common content types, you can usually find a standard or standards-track content type to serve your needs.
If your custom content type generates interest within the developer community, consider proposing it as a standard content type through the XIP process.
Create the content typeβ
Create the custom content type by creating a new file
- JavaScript
- React
- Kotlin
- Swift
- Dart
- React Native
A test of this content type can be found in the following PR
import { ContentTypeId } from "@xmtp/xmtp-js";
import type { ContentCodec, EncodedContent } from "@xmtp/xmtp-js";
// Create a unique identifier for your content type
export const ContentTypeMultiplyNumbers = new ContentTypeId({
authorityId: 'your.domain',
typeId: 'multiply-number',
versionMajor: 1,
versionMinor: 0,
})
// Define the MultiplyNumbers class
export class MultiplyNumbers {
public num1: number
public num2: number
public result: number | undefined
constructor(num1: number, num2: number, result?: number) {
this.num1 = num1
this.num2 = num2
this.result = result
}
}
// Define the MultiplyCodec class
export class ContentTypeMultiplyNumberCodec
implements ContentCodec<MultiplyNumbers>
{
get contentType(): ContentTypeId {
return ContentTypeMultiplyNumbers
}
// The encode method accepts an object of MultiplyNumbers and encodes it as a byte array
encode(numbers: MultiplyNumbers): EncodedContent {
const { num1, num2 } = numbers
return {
type: ContentTypeMultiplyNumbers,
parameters: {},
content: new TextEncoder().encode(JSON.stringify({ num1, num2 })),
}
}
// The decode method decodes the byte array, parses the string into numbers (num1, num2), and returns their product
decode(encodedContent: EncodedContent): MultiplyNumbers {
const decodedContent = new TextDecoder().decode(encodedContent.content)
const { num1, num2 } = JSON.parse(decodedContent)
return new MultiplyNumbers(num1, num2, num1 * num2)
}
fallback(content: MultiplyNumbers): string | undefined {
return `Canβt display number content types. Number was ${JSON.stringify(
content
)}`
//return undefined; if you don't want the content type to be displayed.
}
}
A test of this content type can be found in the following PR
import { ContentTypeId } from "@xmtp/xmtp-js";
import type { ContentCodec, EncodedContent } from "@xmtp/xmtp-js";
// Create a unique identifier for your content type
export const ContentTypeMultiplyNumbers = new ContentTypeId({
authorityId: 'your.domain',
typeId: 'multiply-number',
versionMajor: 1,
versionMinor: 0,
})
// Define the MultiplyNumbers class
export class MultiplyNumbers {
public num1: number
public num2: number
public result: number | undefined
constructor(num1: number, num2: number, result?: number) {
this.num1 = num1
this.num2 = num2
this.result = result
}
}
// Define the MultiplyCodec class
export class ContentTypeMultiplyNumberCodec
implements ContentCodec<MultiplyNumbers>
{
get contentType(): ContentTypeId {
return ContentTypeMultiplyNumbers
}
// The encode method accepts an object of MultiplyNumbers and encodes it as a byte array
encode(numbers: MultiplyNumbers): EncodedContent {
const { num1, num2 } = numbers
return {
type: ContentTypeMultiplyNumbers,
parameters: {},
content: new TextEncoder().encode(JSON.stringify({ num1, num2 })),
}
}
// The decode method decodes the byte array, parses the string into numbers (num1, num2), and returns their product
decode(encodedContent: EncodedContent): MultiplyNumbers {
const decodedContent = new TextDecoder().decode(encodedContent.content)
const { num1, num2 } = JSON.parse(decodedContent)
return new MultiplyNumbers(num1, num2, num1 * num2)
}
fallback(content: MultiplyNumbers): string | undefined {
return `Canβt display number content types. Number was ${JSON.stringify(
content
)}`
//return undefined; if you don't want the content type to be displayed.
}
}
export const multiplyNumbersContentTypeConfig: ContentTypeConfiguration = {
codecs: [new ContentTypeMultiplyNumberCodec()],
contentTypes: [ContentTypeMultiplyNumbers.toString()],
namespace: NAMESPACE, // Replace with the actual namespace you are using
processors: {
[ContentTypeMultiplyNumbers.toString()]: [processMultiplyNumbers],
},
validators: {
[ContentTypeMultiplyNumbers.toString()]: isValidMultiplyNumbersContent,
},
};
// You'll need to define the processMultiplyNumbers and isValidMultiplyNumbersContent functions
// These should be tailored to handle the specific logic of processing and validating
// the content type for multiplying numbers.
function processMultiplyNumbers(content) {
// Define how to process the multiply numbers content
// Example: logging, storing, or manipulating the data
}
function isValidMultiplyNumbersContent(content) {
// Define validation logic for multiply numbers content
// Example: checking if the numbers are valid, not null, etc.
}
import org.json.JSONObject
import org.xmtp.android.library.codecs.ContentTypeId
import org.xmtp.android.library.codecs.ContentTypeIdBuilder
import org.xmtp.android.library.codecs.ContentCodec
import org.xmtp.android.library.codecs.EncodedContent
data class NumberPair(val a: Double, val b: Double, var result: Double = 0.0)
data class MultiplyNumberCodec(
override var contentType: ContentTypeId = ContentTypeIdBuilder.builderFromAuthorityId(
authorityId = "your.domain",
typeId = "multiply-number",
versionMajor = 1,
versionMinor = 0
)
) : ContentCodec<NumberPair> {
override fun encode(content: NumberPair): EncodedContent {
val jsonObject = JSONObject()
jsonObject.put("a", content.a)
jsonObject.put("b", content.b)
return EncodedContent.newBuilder().also {
it.type = contentType
it.content = jsonObject.toString().toByteStringUtf8()
}.build()
}
override fun decode(content: EncodedContent): NumberPair {
val jsonObject = JSONObject(content.content.toStringUtf8())
val a = jsonObject.getDouble("a")
val b = jsonObject.getDouble("b")
val numberPair = NumberPair(a, b, a * b)
return numberPair
}
override fun fallback(content: NumberPair): String? {
return "Error: This app does not support numbers."
}
}
A test of this content type can be found in the following PR
import XMTP
public struct MultiplyNumbers {
public var num1: Double
public var num2: Double
public var result: Double?
public init(num1: Double, num2: Double, result: Double? = nil) {
self.num1 = num1
self.num2 = num2
self.result = result
}
}
public struct MultiplyNumbersCodec: ContentCodec {
public typealias T = MultiplyNumbers
public var contentType: ContentTypeID {
ContentTypeID(authorityID: "example.com", typeID: "number", versionMajor: 1, versionMinor: 1)
}
public func encode(content: MultiplyNumbers, client: Client) throws -> EncodedContent {
var encodedContent = EncodedContent()
encodedContent.type = contentType
encodedContent.parameters["num1"] = String(content.num1)
encodedContent.parameters["num2"] = String(content.num2)
return encodedContent
}
public func decode(content: EncodedContent, client _: Client) throws -> MultiplyNumbers {
guard let num1Str = content.parameters["num1"], let num1 = Double(num1Str),
let num2Str = content.parameters["num2"], let num2 = Double(num2Str) else {
throw CodecError.invalidContent
}
return MultiplyNumbers(num1: num1, num2: num2, result: num1 * num2)
}
public func fallback(content: MultiplyNumbers) throws -> String? {
return "MultiplyNumbersCodec is not supported"
}
}
A test of this content type can be found in the following PR
import 'dart:convert';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';
import 'package:xmtp/xmtp.dart' as xmtp;
import 'package:xmtp/src/content/codec.dart';
import 'package:xmtp/src/client.dart';
// Class to store two numbers and their multiplication result
class MultiplyNumbers {
final double num1;
final double num2;
final double result;
MultiplyNumbers({required this.num1, required this.num2, required this.result});
}
/// This encodes two numbers and their multiplication result.
final contentTypeMultiplyNumbers = xmtp.ContentTypeId(
authorityId: "com.example",
typeId: "multiplyNumbers",
versionMajor: 1,
versionMinor: 1,
);
// Codec class to encode and decode the multiplication result
class MultiplyNumbersCodec extends Codec<MultiplyNumbers> {
// Content type for the codec
xmtp.ContentTypeId get contentType => contentTypeMultiplyNumbers;
// Function to encode the multiplication result
Future<xmtp.EncodedContent> encode(MultiplyNumbers decoded) async => xmtp.EncodedContent(
type: contentTypeMultiplyNumbers,
parameters: {
'num1': decoded.num1.toString(),
'num2': decoded.num2.toString(),
},
);
// Function to decode the encoded content
Future<MultiplyNumbers> decode(xmtp.EncodedContent encoded) async {
var num1 = double.parse(encoded.parameters['num1'] ?? '0');
var num2 = double.parse(encoded.parameters['num2'] ?? '0');
return MultiplyNumbers(num1: num1, num2: num2, result: num1 * num2);
}
// Fallback function in case the codec is not supported
String fallback(MultiplyNumbers content) => "MultiplyNumbersCodec is not supported";
}
import { content } from '@xmtp/proto'
import {JSContentCodec, Client, Conversation, DecodedMessage } from '@xmtp/react-native-sdk';
type EncodedContent = content.EncodedContent
type ContentTypeId = content.ContentTypeId
const ContentTypeMultiplyNumbers: ContentTypeId = {
authorityId: 'com.example',
typeId: 'multiplyNumbers',
versionMajor: 1,
versionMinor: 1,
}
class MultiplyNumbers {
public readonly num1: number
public readonly num2: number
public readonly result: number
constructor(num1: number, num2: number, result: number) {
this.num1 = num1
this.num2 = num2
this.result = result
}
}
class ContentTypeMultiplyNumberCodec
implements JSContentCodec<MultiplyNumbers>
{
get contentType() {
return ContentTypeMultiplyNumbers
}
encode(decoded: MultiplyNumbers): EncodedContent {
return {
type: ContentTypeMultiplyNumbers,
parameters: {
num1: decoded.num1.toString(),
num2: decoded.num2.toString(),
},
content: new Uint8Array(),
}
}
decode(encoded: EncodedContent): MultiplyNumbers {
const num1 = parseFloat(encoded.parameters['num1'] ?? '0')
const num2 = parseFloat(encoded.parameters['num2'] ?? '0')
return new MultiplyNumbers(num1, num2, num1 * num2)
}
fallback(content: MultiplyNumbers): string {
return `MultiplyNumbersCodec is not supported`
}
}
Configure the content typesβ
Import and register the custom content type.
- JavaScript
- React
- Kotlin
- Swift
- Dart
- React Native
import { ContentTypeMultiplyNumberCodec } from "./xmtp-content-type-multiply-number";
const client = await Client.create({
env: "production",
codecs: [new ContentTypeMultiplyNumberCodec()],
});
//or
client.registerCodec(new ContentTypeMultiplyNumberCodec());
import {
XMTPProvider,
} from "@xmtp/react-sdk";
import {multiplyNumbersContentTypeConfig} from "./xmtp-content-type-multiply-number";
const contentTypeConfigs = [
multiplyNumbersContentTypeConfig,
];
createRoot(document.getElementById("root") as HTMLElement).render(
<StrictMode>
<XMTPProvider contentTypeConfigs={contentTypeConfigs}>
<App />
</XMTPProvider>
</StrictMode>,
);
import org.xmtp.android.library.codecs.ContentTypeMultiplyNumberCodec
Client.register(codec = ContentTypeMultiplyNumberCodec())
Client.register(codec: ContentTypeMultiplyNumberCodec())
var client = await Client.createFromWallet(api, wallet,customCodecs: [MultiplyNumbersCodec()]);
import { ContentTypeMultiplyNumberCodec } from "./xmtp-content-type-number";
const client = await Client.create({
env: "production",
codecs: [new ContentTypeMultiplyNumberCodec()],
});
Send the contentβ
Send a message using the custom content type. This code sample demonstrates how to use the MultiplyCodec
custom content type to perform multiplication operations.
- JavaScript
- React
- Kotlin
- Swift
- Dart
- React Native
const numbersToMultiply = new MultiplyNumbers(2, 3);
conversation.send(numbersToMultiply, {
contentType: ContentTypeMultiplyNumbers,
});
const numbersToMultiply = { a: 3, b: 7 };
conversation.send(numbersToMultiply, {
contentType: ContentTypeMultiplyNumbers,
});
import org.xmtp.android.library.codecs.ContentTypeMultiplyNumberCodec
val numbers = NumberPair(
a = 3,
b = 7,
)
conversation.send(
content = numbers,
options = SendOptions(contentType = ContentTypeMultiplyNumberCodec().contentType),
)
let multiplyNumbers = MultiplyNumbers(num1: 3, num2: 2)
try await aliceConversation.send(content: multiplyNumbers, options: .init(contentType: ContentTypeMultiplyNumberCodec().contentType))
// Creating a conversation between Alice and Bob
var aliceConvo = await alice.newConversation(bob.address.toString());
// Multiplying two numbers and storing the result
var multiplyNumbers = MultiplyNumbers(num1: 12, num2: 34, result: 12*34);
// Encoding the multiplication result
var encodedMultiplyNumbers = await MultiplyNumbersCodec().encode(multiplyNumbers);
// Alice sends the encoded message
await alice.sendMessageEncoded(aliceConvo, encodedMultiplyNumbers);
const multiplyNumbers = new MultiplyNumbers(3, 7);
await bobConvo.send(multiplyNumbers, {
contentType: ContentTypeMultiplyNumbers,
});
Receive the contentβ
To use the result of the multiplication operation, add a renderer for the custom content type.
- JavaScript
- React
- Swift
- Kotlin
- Dart
- React Native
if (message.contentType.sameAs(ContentTypeMultiplyNumber)) {
return message.content; // 21
}
if (message.contentType.sameAs(ContentTypeMultiplyNumber)) {
return message.content; // 21
}
if let content: MultiplyNumbers = try? messages[0].content() {
//content.result
}
if (bobMessages[0].contentType == contentTypeMultiplyNumbers) {
(bobMessages[0].content as MultiplyNumbers).result// 408.0
}
Because of this message content is now a function which returns the actual content. You can get that content by call message.content()
now instead of message.content . This may involve more filtering on the message side to make sure you are handling different contentTypes appropriately.
if (message.contentTypeId === "com.example/multiplyNumbers:1.1") {
return message.content(); // 21
}
To handle unsupported content types, refer to the fallback section.