On this page

Unity Chat SDK 2.0.0 migration guide

The 2.0.0 release of the Unity Chat SDK introduces a set of API improvements that make the SDK more consistent, more expressive, and better suited for large-scale chat applications. The changes include a revised channel join/connect lifecycle, entity-specific event names that are easier to discover, structured types for event payloads that previously required manual parsing, and a reworked read-receipt and reaction model.

This guide is for developers who use Unity Chat SDK 1.x.x in existing applications.

If your application uses 1.x.x, it will not compile against 2.0.0 without changes. This guide summarizes the differences between the versions and shows how to migrate to Unity Chat SDK 2.0.0.

Most notable changes include:

  • JoinChannel and Connect replace Join: Joining a channel no longer starts message delivery automatically. Call JoinChannel() to create the membership and Connect() separately to start receiving messages. A matching LeaveChannel() and Disconnect() replace Leave().
  • Renamed events on entities: Events on Channel, User, Membership, and Message objects were renamed to shorter, entity-agnostic names. For example, OnChannelUpdate is now OnUpdated and OnUserUpdated is now OnUpdated. Deletion events are now separate (OnDeleted), and the ChatEntityChangeType parameter was removed from all StreamUpdatesOn callbacks.
  • Typed event payloads: Events that previously delivered raw ChatEvent objects now deliver purpose-built types. OnMessageReported delivers MessageReport, OnMentioned delivers Mention, OnInvited delivers Invite, and OnRestrictionChanged delivers ChannelRestriction.
  • New read receipt format: OnReadReceiptEvent now fires once per individual user receipt rather than aggregating all memberships into a dictionary. A new ReadReceipt struct carries UserId and LastReadTimeToken. A GetReadReceipts() method provides the full snapshot when needed.
  • New message reaction format: A new MessageReactions() method returns reactions grouped by value with a Count and an IsMine flag, replacing the need to iterate raw MessageAction lists.
  • SendTextParams simplified: MentionedUsers, QuotedMessage, and Files were removed from SendTextParams and must now be set through MessageDraft.
  • Soft deletion deprecated: The bool soft parameter on delete methods for Users and Channels is deprecated. Hard delete is now the default behavior.

Differences between 1.x.x and 2.0.0

Feature/Method1.x.x2.0.0
Joining a channel
Join() implicitly calls Connect()
JoinChannel() and Connect() are separate calls
Leaving a channel
Leave()
LeaveChannel() and Disconnect()
Channel update event
OnChannelUpdate
OnUpdated
Channel/user/membership deletion event
Part of the OnUpdate callback with ChatEntityChangeType
Separate OnDeleted event
Message update event
OnMessageUpdated
OnUpdated
User update event
OnUserUpdated
OnUpdated
Membership update event
OnMembershipUpdated
OnUpdated
StreamUpdatesOn callback signature
Action<T, ChatEntityChangeType>
Action<T> (change type removed)
Report event on Channel
OnReportEvent with raw ChatEvent payload
OnMessageReported with typed MessageReport
Mention event on User
OnMentionEvent with raw ChatEvent payload
OnMentioned with typed Mention
Invite event on User
OnInviteEvent with raw ChatEvent payload
OnInvited with typed Invite
Moderation event on User
OnModerationEvent with raw ChatEvent payload
OnRestrictionChanged with typed ChannelRestriction
Read receipt event payload
Dictionary<string, List<string>> (timetoken → user IDs)
ReadReceipt { UserId, LastReadTimeToken }
Read receipt snapshot
No dedicated method
Channel.GetReadReceipts()
Message reactions
message.Reactions → raw List<MessageAction>
message.MessageReactions()List<MessageReaction> with Count and IsMine
Mentioning, quoting, and files in SendText
Set on SendTextParams
Set on MessageDraft before calling draft.Send()
Custom events
chat.EmitEvent(...) (public)
chat.EmitEvent(...) is now internal; use channel.EmitCustomEvent(...)
Emitting user mentions
channel.EmitUserMention(...) (public)
channel.EmitUserMention(...) is now protected
Soft delete
Delete(bool soft = false)
bool soft parameter deprecated for Users and Channels; delete is hard by default
Read receipt emission control
Always emitted
Controlled per channel type via PubnubChatConfig.EmitReadReceiptEvents
CreateDirectConversation / CreateGroupConversation membership data
Single membershipData parameter
hostMembershipData + inviteeMembershipData parameters
Membership deletion
Not available from Membership object
membership.Delete()

JoinChannel and Connect

Important change

This change affects any code that calls Join() and then listens for messages. Update it before releasing to users.

In 1.x.x, calling Join() on a channel also subscribed to that channel automatically — messages started arriving immediately. In 2.0.0, JoinChannel() creates the membership server-side but does not start message delivery. Call Connect() separately after JoinChannel() to begin receiving messages.

The reverse operation has also changed: use LeaveChannel() and Disconnect() instead of Leave().

1// Join implicitly started message delivery
2await channel.Join();
3
4// Messages arrived via channel.OnMessageReceived

Renamed events on Channel

Several Channel events have been renamed and split for clarity.

OnChannelUpdate is renamed to OnUpdated. The previously overloaded OnUpdate callback (which received a ChatEntityChangeType parameter) is removed — hard deletion is now signalled by the new OnDeleted event.

OnReportEvent is renamed to OnMessageReported. The payload changes from a raw ChatEvent to a structured MessageReport.

OnReadReceiptEvent remains, but its payload type changes from Dictionary<string, List<string>> to the new ReadReceipt struct. See Read receipts for full details.

1channel.OnChannelUpdate += (updatedChannel) =>
2{
3 Debug.Log($"Channel updated: {updatedChannel.Id}");
4};
5
6channel.OnUpdate += (updatedChannel, changeType) =>
7{
8 if (changeType == ChatEntityChangeType.Delete)
9 {
10 Debug.Log("Channel was deleted");
11 }
12};
13
14channel.OnReportEvent += (chatEvent) =>
15{
show all 17 lines

Renamed events on User

OnUserUpdated is renamed to OnUpdated. Deletion is now signalled by the separate OnDeleted event rather than through the overloaded OnUpdate callback.

Events that previously delivered raw ChatEvent objects now carry purpose-built types:

Old eventNew eventNew payload type
OnMentionEvent
OnMentioned
Mention
OnInviteEvent
OnInvited
Invite
OnModerationEvent
OnRestrictionChanged
ChannelRestriction
1user.OnUserUpdated += (updatedUser) => { Debug.Log(updatedUser.Id); };
2
3user.OnMentionEvent += (chatEvent) =>
4{
5 Debug.Log($"Mentioned in: {chatEvent.ChannelId}");
6};
7
8user.OnInviteEvent += (chatEvent) =>
9{
10 Debug.Log($"Invited to: {chatEvent.ChannelId}");
11};
12
13user.OnModerationEvent += (chatEvent) =>
14{
15 Debug.Log($"Moderation event: {chatEvent.Payload}");
show all 16 lines

Renamed events on Membership and Message

OnMembershipUpdated on Membership is renamed to OnUpdated. OnMessageUpdated on Message is renamed to OnUpdated. Both entities gain a separate OnDeleted event for hard deletion.

1// Before
2membership.OnMembershipUpdated += (m) => { };
3message.OnMessageUpdated += (m) => { };
4
5// After
6membership.OnUpdated += (m) => { };
7membership.OnDeleted += () => { };
8
9message.OnUpdated += (m) => { };

StreamUpdatesOn callback signatures

The ChatEntityChangeType parameter has been removed from all StreamUpdatesOn callback signatures. Update callbacks on Channel, User, Membership, and Message.

1Channel.StreamUpdatesOn(channels, (channel, changeType) =>
2{
3 Debug.Log($"Channel changed: {changeType}");
4});
5
6Membership.StreamUpdatesOn(memberships, (membership, changeType) =>
7{
8 Debug.Log($"Membership changed: {changeType}");
9});

The same change applies to User.StreamUpdatesOn and Message.StreamUpdatesOn.

Read receipts

Important change

This change affects any code that reads OnReadReceiptEvent payload. Update the handler before releasing to users.

In 1.x.x, OnReadReceiptEvent fired with a Dictionary<string, List<string>> where keys were timetokens and values were lists of user IDs who had read up to that point. Computing this required a call to GetChannelMemberships on every receipt event.

In 2.0.0:

  • OnReadReceiptEvent fires once per incoming receipt signal, delivering a single ReadReceipt value that contains the user ID and their last-read timetoken. No membership fetch is required.
  • Channel.GetReadReceipts() provides an on-demand snapshot of all current read receipts as List<ReadReceipt>.
1channel.OnReadReceiptEvent += (Dictionary<string, List<string>> receipts) =>
2{
3 foreach (var kvp in receipts)
4 {
5 string timetoken = kvp.Key;
6 List<string> userIds = kvp.Value;
7 Debug.Log($"Timetoken {timetoken} read by {userIds.Count} users");
8 }
9};

Controlling which channel types emit read receipts

A new EmitReadReceiptEvents property in PubnubChatConfig controls which channel types produce read receipt signals. The default setting disables read receipts on public channels, which is appropriate for most large-scale deployments.

1// Default: public = false, group = true, direct = true
2// To restore old behavior (always emit), set all to true:
3var config = new PubnubChatConfig(publishKey, subscribeKey, userId,
4 emitReadReceiptEvents: new Dictionary<string, bool>
5 {
6 { "public", true },
7 { "group", true },
8 { "direct", true }
9 });

Message reactions

In 1.x.x, the raw message.Reactions property returned a flat List<MessageAction>. Grouping reactions by type, counting them, or checking whether the current user had reacted required manual processing.

In 2.0.0, the new message.MessageReactions() method returns List<MessageReaction>. Each MessageReaction aggregates reactions of the same value and exposes Count (how many users reacted) and IsMine (whether the current user is among them). The raw message.Reactions property remains available for backwards-compatible reading.

1// Raw list — manual grouping required
2var reactions = message.Reactions;
3var grouped = reactions
4 .GroupBy(r => r.Value)
5 .Select(g => new { Value = g.Key, Count = g.Count() });
6
7foreach (var group in grouped)
8{
9 Debug.Log($"{group.Value}: {group.Count}");
10}

SendTextParams and MessageDraft

MentionedUsers, QuotedMessage, and Files have been removed from SendTextParams and are now properties on MessageDraft. To send a message with mentions, a quoted message, or attachments, create a draft, configure it, and send.

1await channel.SendText("See you there!", new SendTextParams
2{
3 QuotedMessage = someMessage,
4 MentionedUsers = new Dictionary<int, User> { { 8, mentionedUser } },
5 Files = new List<InputFile> { myFile }
6});

A new AppendText() convenience method is also available:

1draft.AppendText(" — see you there!");

Custom events

chat.EmitEvent() is now internal and can no longer be called from application code. Use channel.EmitCustomEvent() instead, passing the channel you want to emit to directly.

1await chat.EmitEvent(PubnubChatEventType.Custom, "my-channel", "{\"key\":\"value\"}");

CreateDirectConversation and CreateGroupConversation

Both conversation-creation methods now accept separate membership data for the host and for the invited user(s).

1await chat.CreateDirectConversation(user, channelId, channelData,
2 membershipData: hostMembershipData);
3
4await chat.CreateGroupConversation(users, channelId, channelData,
5 membershipData: hostMembershipData);

Soft deletion deprecated

The bool soft parameter on delete methods is deprecated. If you rely on soft deletion (marking entities as deleted while retaining data), migrate to explicit hard deletion using the new parameter-free Delete() methods. If you need to preserve the soft-delete behavior during a transitional period, the old overloads still compile, but they will be removed in a future version.

1// Deprecated — soft parameter will be removed
2await user.DeleteUser(soft: true);
3await channel.Delete(soft: false);
4
5// Preferred in 2.0.0
6await user.Delete();
7await channel.Delete();
8await membership.Delete(); // new: Delete() is now available directly on Membership

New capabilities in 2.0.0

The following features are new in 2.0.0 and have no counterpart in 1.x.x.

FeatureDescription
GetReadReceipts()
await channel.GetReadReceipts() returns a paginated List<ReadReceipt> snapshot of the current read state for all channel members.
MessageReactions()
message.MessageReactions() returns reactions grouped by value with Count and IsMine fields.
HasMember() / GetMember()
await channel.HasMember(userId) and await channel.GetMember(userId) let you check and retrieve a specific member without fetching the full membership list.
IsMemberOn() / GetMembership()
await user.IsMemberOn(channelId) and await user.GetMembership(channelId) let you check and retrieve a specific membership from the User object.
Membership.Delete()
await membership.Delete() deletes a membership directly from the Membership object.
ReconnectSubscriptions() / DisconnectSubscriptions()
chat.ReconnectSubscriptions() restores all active subscriptions; chat.DisconnectSubscriptions() drops them cleanly.
OnSubscriptionStatusChanged
chat.OnSubscriptionStatusChanged fires with a PNStatus value whenever the underlying PubNub connection changes. Enable it with chat.StreamSubscriptionStatus(true).
ChatUtils (public)
ChatUtils.TimeTokenNow() and ChatUtils.TimeToken(DateTime date) are now publicly accessible.
MessageDraft.AppendText()
Appends a plain-text string to the end of a draft without needing to compute the current cursor position.
MessageDraft.GetMessageElements()
MessageDraft.GetMessageElements(text) (static) and message.GetMessageElements() parse a message text into a structured List<MessageElement> for rendering mentions and links.
EmitReadReceiptEvents config
PubnubChatConfig.EmitReadReceiptEvents (Dictionary<string, bool>) controls which channel types emit read receipt signals, letting you reduce unnecessary network traffic on public channels.

Migration steps

To migrate from Unity Chat SDK 1.x.x to 2.0.0:

  1. Replace every channel.Join() call with await channel.JoinChannel() followed by channel.Connect(). Replace every channel.Leave() call with channel.Disconnect() followed by await channel.LeaveChannel().
  2. Rename OnChannelUpdate to OnUpdated on Channel. Remove any OnUpdate handlers that checked for ChatEntityChangeType.Delete and replace them with OnDeleted handlers. Apply the same pattern to User, Membership, and Message.
  3. Rename OnMessageUpdated on Message to OnUpdated.
  4. Rename OnUserUpdated on User to OnUpdated.
  5. Rename OnMembershipUpdated on Membership to OnUpdated.
  6. Update StreamUpdatesOn callbacks on Channel, User, Membership, and Message to remove the ChatEntityChangeType parameter.
  7. Rename OnMentionEvent to OnMentioned on User and update the handler to use the Mention type. Rename OnInviteEvent to OnInvited (use Invite type). Rename OnModerationEvent to OnRestrictionChanged (use ChannelRestriction type).
  8. Rename OnReportEvent to OnMessageReported on Channel and update the handler to use the MessageReport type.
  9. Update all OnReadReceiptEvent handlers to use the new ReadReceipt struct (UserId, LastReadTimeToken) instead of the old dictionary type. Replace any manual membership-aggregation logic with GetReadReceipts() where a full snapshot is needed.
  10. Replace raw message.Reactions iteration with message.MessageReactions() where reaction counts or IsMine checks are needed.
  11. Move QuotedMessage, MentionedUsers, and Files out of SendTextParams and onto a MessageDraft instance. Call draft.Send() instead of channel.SendText() for messages that use these features.
  12. Replace chat.EmitEvent(...) calls with channel.EmitCustomEvent(...).
  13. Update CreateDirectConversation and CreateGroupConversation calls to use the renamed hostMembershipData and the new inviteeMembershipData / inviteesMembershipData parameters.
  14. Remove the bool soft parameter from any DeleteUser(), Channel.Delete(), or Chat.DeleteChannel() calls. Migrate to the new parameter-free Delete() methods.
  15. Review PubnubChatConfig initialization and add EmitReadReceiptEvents if you need non-default read receipt emission behavior.

If you encounter issues during migration, contact PubNub Support.

Last updated on