To use this feature, you need to subscribe to the Enterprise plan.
Setting conversation message timed destruct means that after setting a timed destruct duration for a conversation, messages sent in that conversation will be automatically deleted after the specified duration. This can be used for scenarios such as read-and-delete (burn-after-reading) and confidential communication.
duration: Timed destruct duration, in seconds. The supported range is 0 or 60 ~ 604800 (7 days). Setting it to 0 means canceling timed destruct.
Calling timing: Can be called after the user logs in, and will succeed when network conditions are good.
To set timed destruct for a peer chat conversation, the conversationID must correspond to a registered user.
To set timed destruct for a group chat conversation, the group must have been created and the current user must have joined the group.
Note
The timed destruct setting for group chat conversations is by default configurable by all group members, and can be changed to only the group owner and administrators. To change the default permission configuration, contact ZEGO technical support.
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
zim.setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType.PEER,
new ZIMConversationMessageDestructDurationSetCallback() {
@Override
public void onConversationMessageDestructDurationSet(
String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
// Set successfully
} else {
// Set failed
}
}
});
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
zim.setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType.PEER,
new ZIMConversationMessageDestructDurationSetCallback() {
@Override
public void onConversationMessageDestructDurationSet(
String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
// Set successfully
} else {
// Set failed
}
}
});
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
[zim setConversationMessageDestructDuration:duration
conversationID:conversationID
conversationType:ZIMConversationTypePeer
callback:^(NSString *conversationID, ZIMConversationType conversationType, ZIMError *errorInfo) {
if (errorInfo.code == 0) {
// Set successfully
} else {
// Set failed
}
}];
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
[zim setConversationMessageDestructDuration:duration
conversationID:conversationID
conversationType:ZIMConversationTypePeer
callback:^(NSString *conversationID, ZIMConversationType conversationType, ZIMError *errorInfo) {
if (errorInfo.code == 0) {
// Set successfully
} else {
// Set failed
}
}];
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
zim->setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
[=](const std::string &conversationID, ZIMConversationType conversationType, const ZIMError &errorInfo) {
if (errorInfo.code == 0) {
// Set successfully
} else {
// Set failed
}
});
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
zim->setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
[=](const std::string &conversationID, ZIMConversationType conversationType, const ZIMError &errorInfo) {
if (errorInfo.code == 0) {
// Set successfully
} else {
// Set failed
}
});
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
ZIM.getInstance()!
.setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType.peer)
.then((value) {
// Set successfully
})
.catchError((onError) {
// Set failed
});
// duration: In seconds. Supported range: 0 or 60-604800 (7d), 0 = cancel timed destruct
int duration = 300; // For example, set to destruct after 5 minutes
ZIM.getInstance()!
.setConversationMessageDestructDuration(duration, conversationID, ZIMConversationType.peer)
.then((value) {
// Set successfully
})
.catchError((onError) {
// Set failed
});
Get conversation message timed destruct configuration
When the timed destruct duration of a conversation changes (including changes set locally or synced from other devices), you can detect the change through the conversationChanged callback.
ZIMConversation has a new messageDestructDuration property, which indicates the timed destruct duration of the current conversation. For conversations where timed destruct has never been set, this value is 0.
// 1. Listen for conversation change events
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onConversationChanged(ZIM zim, ArrayList<ZIMConversationChangeInfo> conversationChangeInfoList) {
for (ZIMConversationChangeInfo info : conversationChangeInfoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
}
});
// 2. You can also query a specific conversation using the queryConversation API
zim.queryConversation(conversationID, ZIMConversationType.PEER, new ZIMConversationQueriedCallback() {
@Override
public void onConversationQueried(ZIMConversation conversation, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
int duration = conversation.messageDestructDuration;
}
}
});
// 1. Listen for conversation change events
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onConversationChanged(ZIM zim, ArrayList<ZIMConversationChangeInfo> conversationChangeInfoList) {
for (ZIMConversationChangeInfo info : conversationChangeInfoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
}
});
// 2. You can also query a specific conversation using the queryConversation API
zim.queryConversation(conversationID, ZIMConversationType.PEER, new ZIMConversationQueriedCallback() {
@Override
public void onConversationQueried(ZIMConversation conversation, ZIMError errorInfo) {
if (errorInfo.code == ZIMErrorCode.SUCCESS) {
int duration = conversation.messageDestructDuration;
}
}
});
// 1. Listen for conversation change events
- (void)zim:(ZIM *)zim conversationChanged:(NSArray<ZIMConversationChangeInfo *> *)infoList {
for (ZIMConversationChangeInfo *info in infoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
}
// 2. You can also query a specific conversation using the queryConversation API
[zim queryConversationWithConversationID:conversationID
conversationType:ZIMConversationTypePeer
callback:^(ZIMConversation *conversation, ZIMError *errorInfo) {
if (errorInfo.code == 0) {
int duration = conversation.messageDestructDuration;
}
}];
// 1. Listen for conversation change events
- (void)zim:(ZIM *)zim conversationChanged:(NSArray<ZIMConversationChangeInfo *> *)infoList {
for (ZIMConversationChangeInfo *info in infoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
}
// 2. You can also query a specific conversation using the queryConversation API
[zim queryConversationWithConversationID:conversationID
conversationType:ZIMConversationTypePeer
callback:^(ZIMConversation *conversation, ZIMError *errorInfo) {
if (errorInfo.code == 0) {
int duration = conversation.messageDestructDuration;
}
}];
// 1. Listen for conversation change events
zim->onConversationChanged = [](ZIM *zim, const std::vector<ZIMConversationChangeInfo> &infoList) {
for (auto &info : infoList) {
int duration = info.conversation.getMessageDestructDuration();
// Update UI based on duration
}
};
// 2. You can also query a specific conversation using the queryConversation API
zim->queryConversation(conversationID, ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
[=](const std::shared_ptr<ZIMConversation> &conversation, const ZIMError &errorInfo) {
if (errorInfo.code == 0) {
int duration = conversation->getMessageDestructDuration();
}
});
// 1. Listen for conversation change events
zim->onConversationChanged = [](ZIM *zim, const std::vector<ZIMConversationChangeInfo> &infoList) {
for (auto &info : infoList) {
int duration = info.conversation.getMessageDestructDuration();
// Update UI based on duration
}
};
// 2. You can also query a specific conversation using the queryConversation API
zim->queryConversation(conversationID, ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
[=](const std::shared_ptr<ZIMConversation> &conversation, const ZIMError &errorInfo) {
if (errorInfo.code == 0) {
int duration = conversation->getMessageDestructDuration();
}
});
// 1. Listen for conversation change events
ZIMEventHandler.onConversationChanged = (ZIM zim, List<ZIMConversationChangeInfo> conversationChangeInfoList) {
for (var info in conversationChangeInfoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
};
// 2. You can also query a specific conversation using the queryConversation API
ZIM.getInstance()!
.queryConversation(conversationID, ZIMConversationType.peer)
.then((conversation) {
int duration = conversation.messageDestructDuration;
})
.catchError((onError) {});
// 1. Listen for conversation change events
ZIMEventHandler.onConversationChanged = (ZIM zim, List<ZIMConversationChangeInfo> conversationChangeInfoList) {
for (var info in conversationChangeInfoList) {
int duration = info.conversation.messageDestructDuration;
// Update UI based on duration
}
};
// 2. You can also query a specific conversation using the queryConversation API
ZIM.getInstance()!
.queryConversation(conversationID, ZIMConversationType.peer)
.then((conversation) {
int duration = conversation.messageDestructDuration;
})
.catchError((onError) {});
Get the expiration timestamp of timed destruct messages
After setting a timed destruct duration for a conversation, messages subsequently sent by members in that conversation will carry an expiration timestamp destructTime. destructTime is in milliseconds, and 0 indicates that the message is not a timed destruct message.
Developers can use destructTime along with the current time to get the countdown for the corresponding message's destruct time.
// ZIMMessage new property
// public long destructTime; // In milliseconds, 0 indicates non-timed destruct message
long destructTime = message.destructTime;
if (destructTime > 0) {
long countdown = destructTime - System.currentTimeMillis();
// Display countdown
}
// ZIMMessage new property
// public long destructTime; // In milliseconds, 0 indicates non-timed destruct message
long destructTime = message.destructTime;
if (destructTime > 0) {
long countdown = destructTime - System.currentTimeMillis();
// Display countdown
}
// ZIMMessage new property
// @property (nonatomic, assign) long long destructTime; // In milliseconds, 0 indicates non-timed destruct message
long long destructTime = message.destructTime;
if (destructTime > 0) {
long long countdown = destructTime - [[NSDate date] timeIntervalSince1970] * 1000;
// Display countdown
}
// ZIMMessage new property
// @property (nonatomic, assign) long long destructTime; // In milliseconds, 0 indicates non-timed destruct message
long long destructTime = message.destructTime;
if (destructTime > 0) {
long long countdown = destructTime - [[NSDate date] timeIntervalSince1970] * 1000;
// Display countdown
}
// ZIMMessage new method
// long long getDestructTime() { return destructTime; } // In milliseconds, 0 indicates non-timed destruct message
long long destructTime = message->getDestructTime();
if (destructTime > 0) {
// Display countdown
}
// ZIMMessage new method
// long long getDestructTime() { return destructTime; } // In milliseconds, 0 indicates non-timed destruct message
long long destructTime = message->getDestructTime();
if (destructTime > 0) {
// Display countdown
}
// ZIMMessage new property
// int destructTime; // In milliseconds, 0 indicates non-timed destruct message
int destructTime = message.destructTime;
if (destructTime > 0) {
int countdown = destructTime - DateTime.now().millisecondsSinceEpoch;
// Display countdown
}
// ZIMMessage new property
// int destructTime; // In milliseconds, 0 indicates non-timed destruct message
int destructTime = message.destructTime;
if (destructTime > 0) {
int countdown = destructTime - DateTime.now().millisecondsSinceEpoch;
// Display countdown
}
Get a more accurate message destruct countdown
If you need a more accurate countdown, you can get the current SDK timestamp through the callExperimentalAPIWithParams API, then calculate the countdown using destructTime - SDK current timestamp.
The input parameter is a JSON string:
{
"method": "getSDKTimestamp"
}
{
"method": "getSDKTimestamp"
}
The SDK returns the result (the result parameter of ZIMExperimentalAPICalledCallback) as JSON:
{
"timestamp": 123456
}
{
"timestamp": 123456
}
Where timestamp is in milliseconds.
Listen for timed destruct messages
When a message reaches its destruct time, the onMessageDeleted event will be triggered, with the delete type ZIMMessageDeleteType being MessagesDestructed.
Developers can listen for this callback and remove the corresponding message from the message list.
// ZIMMessageDeleteType enum
// MessageListDeleted = 0 // User manually deleted
// ConversationAllMessagesDeleted = 1 // Clear conversation messages
// AllConversationMessagesDeleted = 2 // Clear all conversations
// MessagesDestructed = 3 // Message timed destruct
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onMessageDeleted(ZIM zim, ZIMMessageDeletedInfo deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType.MessagesDestructed) {
// Message deleted due to timed destruct
for (ZIMMessage message : deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
}
});
// ZIMMessageDeleteType enum
// MessageListDeleted = 0 // User manually deleted
// ConversationAllMessagesDeleted = 1 // Clear conversation messages
// AllConversationMessagesDeleted = 2 // Clear all conversations
// MessagesDestructed = 3 // Message timed destruct
zim.setEventHandler(new ZIMEventHandler() {
@Override
public void onMessageDeleted(ZIM zim, ZIMMessageDeletedInfo deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType.MessagesDestructed) {
// Message deleted due to timed destruct
for (ZIMMessage message : deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
}
});
// ZIMMessageDeleteType enum
// ZIMMessageDeleteTypeMessageListDeleted = 0 // User manually deleted
// ZIMMessageDeleteTypeConversationAllMessagesDeleted = 1 // Clear conversation messages
// ZIMMessageDeleteTypeAllConversationMessagesDeleted = 2 // Clear all conversations
// ZIMMessageDeleteTypeMessagesDestructed = 3 // Message timed destruct
- (void)zim:(ZIM *)zim messageDeleted:(ZIMMessageDeletedInfo *)deletedInfo {
if (deletedInfo.deleteType == ZIMMessageDeleteTypeMessagesDestructed) {
// Message deleted due to timed destruct
for (ZIMMessage *message in deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
}
// ZIMMessageDeleteType enum
// ZIMMessageDeleteTypeMessageListDeleted = 0 // User manually deleted
// ZIMMessageDeleteTypeConversationAllMessagesDeleted = 1 // Clear conversation messages
// ZIMMessageDeleteTypeAllConversationMessagesDeleted = 2 // Clear all conversations
// ZIMMessageDeleteTypeMessagesDestructed = 3 // Message timed destruct
- (void)zim:(ZIM *)zim messageDeleted:(ZIMMessageDeletedInfo *)deletedInfo {
if (deletedInfo.deleteType == ZIMMessageDeleteTypeMessagesDestructed) {
// Message deleted due to timed destruct
for (ZIMMessage *message in deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
}
// ZIMMessageDeleteType enum
// enum class ZIMMessageDeleteType {
// MessageListDeleted = 0,
// ConversationAllMessagesDeleted = 1,
// AllConversationMessagesDeleted = 2,
// MessagesDestructed = 3,
// };
zim->onMessageDeleted = [](ZIM *zim, const ZIMMessageDeletedInfo &deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType::MessagesDestructed) {
// Message deleted due to timed destruct
for (auto &message : deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
};
// ZIMMessageDeleteType enum
// enum class ZIMMessageDeleteType {
// MessageListDeleted = 0,
// ConversationAllMessagesDeleted = 1,
// AllConversationMessagesDeleted = 2,
// MessagesDestructed = 3,
// };
zim->onMessageDeleted = [](ZIM *zim, const ZIMMessageDeletedInfo &deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType::MessagesDestructed) {
// Message deleted due to timed destruct
for (auto &message : deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
};
// ZIMMessageDeleteType enum
// messageListDeleted = 0 // User manually deleted
// conversationAllMessagesDeleted = 1 // Clear conversation messages
// allConversationMessagesDeleted = 2 // Clear all conversations
// messagesDestructed = 3 // Message timed destruct
ZIMEventHandler.onMessageDeleted = (ZIM zim, ZIMMessageDeletedInfo deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType.messagesDestructed) {
// Message deleted due to timed destruct
for (var message in deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
};
// ZIMMessageDeleteType enum
// messageListDeleted = 0 // User manually deleted
// conversationAllMessagesDeleted = 1 // Clear conversation messages
// allConversationMessagesDeleted = 2 // Clear all conversations
// messagesDestructed = 3 // Message timed destruct
ZIMEventHandler.onMessageDeleted = (ZIM zim, ZIMMessageDeletedInfo deletedInfo) {
if (deletedInfo.deleteType == ZIMMessageDeleteType.messagesDestructed) {
// Message deleted due to timed destruct
for (var message in deletedInfo.messageList) {
// Remove the corresponding message from the message list
}
}
};