mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-17 08:41:55 +00:00
11 KiB
11 KiB
Group Lifecycle Flow
Related spec: spec/api.md | spec/database.md
Overview
Complete group management in SimpleX Chat iOS: creating groups, inviting members, joining via links, managing roles and admission, and group deletion. Groups use the same E2E encryption as direct messages -- each member pair has independent encrypted channels. Group metadata (name, image, preferences) is distributed via the group protocol.
Prerequisites
- User profile created and chat engine running
- At least one established contact (to invite to a group)
- For joining via link: a valid group link or invitation
Step-by-Step Processes
1. Create Group
- User taps "+" in
ChatListView->NewChatMenuButton-> "Create group". AddGroupViewis presented for entering group name, optional image, and description.- User fills in
GroupProfile(displayName:fullName:image:description:)and taps "Create". - Calls
apiNewGroup(incognito:groupProfile:):func apiNewGroup(incognito: Bool, groupProfile: GroupProfile) throws -> GroupInfo - Sends
ChatCommand.apiNewGroup(userId:incognito:groupProfile:)to core (synchronous). - Core returns
ChatResponse2.groupCreated(user, groupInfo). GroupInfocontains the new group's ID, profile, and the creator as owner.- User is navigated to
AddGroupMembersViewto optionally invite contacts. - User can also create a group link at this stage.
1a. Create Public Group (Channel)
- Alternative to standard group creation for relay-backed channels.
- Calls
apiNewPublicGroup(incognito:relayIds:groupProfile:):func apiNewPublicGroup(incognito: Bool, relayIds: [Int64], groupProfile: GroupProfile) async throws -> (GroupInfo, GroupLink, [GroupRelay]) - Sends
ChatCommand.apiNewPublicGroup(userId:incognito:relayIds:groupProfile:)to core. - Core returns
ChatResponse2.publicGroupCreated(user, groupInfo, groupLink, groupRelays). - The resulting
GroupInfohasuseRelays == trueand includes a group link. - Channel relay members (with role
.relay) are managed by the core.
2. Invite Members
- From
GroupChatInfoView, user taps "Add members" ->AddGroupMembersView. filterMembersToAddfilters contacts already in the group.- User selects contacts and assigns roles (default:
.member). - For each selected contact, calls
apiAddMember(groupId:contactId:memberRole:):func apiAddMember(_ groupId: Int64, _ contactId: Int64, _ memberRole: GroupMemberRole) async throws -> GroupMember - Core sends group invitation to the contact and returns
ChatResponse2.sentGroupInvitation(user, _, _, member). - The invited contact receives a
CIGroupInvitationViewin their chat. - Invited member's status is
.inviteduntil they accept.
3. Join via Link
- User receives a group link (scanned or pasted).
apiConnectPlanvalidates the link and identifies it as a group link.- For prepared groups (short links):
apiPrepareGroup(connLink:directLink:groupShortLinkData:)shows group info before joining.directLinkistruefor standard group links,falsefor channel relay links. apiConnectPreparedGroup(groupId:incognito:msg:)orapiConnect(incognito:connLink:)initiates joining.- Core processes the join request. Depending on group admission settings:
- Auto-join: Member is added immediately.
- Approval required: Member enters pending admission queue.
apiJoinGroup(groupId:)is called for invitation-based joins:func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult?- Returns one of:
.joined(groupInfo:)-- successfully joined.invitationRemoved-- invitation was revoked (SMP AUTH error).groupNotFound-- group no longer exists
4. Member Admission
- Group has admission settings configured via
MemberAdmissionView. - When a new member joins a group requiring approval, admins see pending members.
- Admin reviews pending member in the member list.
- To accept:
apiAcceptMember(groupId:groupMemberId:memberRole:):func apiAcceptMember(_ groupId: Int64, _ groupMemberId: Int64, _ memberRole: GroupMemberRole) async throws -> (GroupInfo, GroupMember) - Core returns
ChatResponse2.memberAccepted(user, groupInfo, member). - To reject: remove the pending member (same as member removal).
- Member support chat (
MemberSupportView,MemberSupportChatToolbar) allows admins to communicate with pending members.
5. Change Member Roles
- Admin/owner navigates to member info in
GroupChatInfoView. - Selects new role for the member.
- Calls
apiMembersRole(groupId:memberIds:memberRole:):func apiMembersRole(_ groupId: Int64, _ memberIds: [Int64], _ memberRole: GroupMemberRole) async throws -> [GroupMember] - Core returns
ChatResponse2.membersRoleUser(user, _, members, _). - Available roles (in hierarchy order):
.owner-- full control, can delete group.admin-- can manage members, change roles (below admin).moderator-- can delete messages, moderate content.member-- standard participant, can send messages.observer-- read-only access
- Role changes are broadcast to all group members as group events.
6. Remove Member
- Admin/owner navigates to member info -> taps "Remove".
- Calls
apiRemoveMembers(groupId:memberIds:withMessages:):func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool) async throws -> (GroupInfo, [GroupMember]) withMessages: truealso deletes all messages from that member.- Core returns
ChatResponse2.userDeletedMembers(user, updatedGroupInfo, members, withMessages). - Removed member receives notification and loses access.
7. Block Member for All
- Admin can block a member's messages from being visible to all group members.
- Calls
apiBlockMembersForAll(groupId:memberIds:blocked:):func apiBlockMembersForAll(_ groupId: Int64, _ memberIds: [Int64], _ blocked: Bool) async throws -> [GroupMember] - Core returns
ChatResponse2.membersBlockedForAllUser(user, _, members, _).
8. Leave Group
- User navigates to
GroupChatInfoView-> taps "Leave group". - Confirmation dialog is presented.
- Calls
leaveGroup(groupId:)which wrapsapiLeaveGroup(groupId:):func apiLeaveGroup(_ groupId: Int64) async throws -> GroupInfo - Core returns
ChatResponse2.leftMemberUser(user, groupInfo). ChatModel.shared.updateGroup(groupInfo)updates the UI.- User retains local chat history but can no longer send/receive.
9. Delete Group
- Owner navigates to
GroupChatInfoView-> taps "Delete group". - Calls
apiDeleteChat(type: .group, id: groupId):func apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws - Core notifies all members and removes the group.
- Chat is removed from
ChatModel.shared.chats.
10. Group Link Management
Create group link:
- From
GroupLinkView(accessible viaGroupChatInfoView). - Calls
apiCreateGroupLink(groupId:memberRole:):func apiCreateGroupLink(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> GroupLink? - Returns
GroupLinkcontaining the link URI and member role. - Optional:
apiAddGroupShortLink(groupId:)generates an additional short link.
Update link role:
apiGroupLinkMemberRole(groupId:memberRole:)changes the default role for new joiners.
Delete group link:
apiDeleteGroupLink(groupId:)invalidates the link.
Get existing link:
apiGetGroupLink(groupId:)retrieves the current link (returnsnilif none exists).
11. Group Preferences
GroupPreferencesViewallows configuring per-feature preferences.- Features controlled include:
- Timed/disappearing messages
- Message reactions
- Voice messages
- File sharing
- Direct messages between members
- Full message deletion
- Message history visibility for new members
- Changes are saved via
apiUpdateGroup(groupId:groupProfile:)with updated preferences. GroupWelcomeViewmanages the welcome message shown to new joiners.
Data Structures
| Type | Location | Description |
|---|---|---|
GroupInfo |
SimpleXChat/ChatTypes.swift |
Full group model: ID, profile, membership, preferences, business chat info |
GroupProfile |
SimpleXChat/ChatTypes.swift |
Name, full name, image, description, preferences |
GroupMember |
SimpleXChat/ChatTypes.swift |
Member model: role, status, profile, connection info |
GroupMemberRole |
SimpleXChat/ChatTypes.swift |
.owner, .admin, .moderator, .member, .observer, .relay |
GroupMemberStatus |
SimpleXChat/ChatTypes.swift |
Member lifecycle: .invited, .accepted, .connected, .complete, etc. |
GroupLink |
Shared/Model/AppAPITypes.swift |
Group link with URI, member role, and short link data |
BusinessChatInfo |
SimpleXChat/ChatTypes.swift |
Business chat metadata for commercial group chats |
JoinGroupResult |
Shared/Model/SimpleXAPI.swift |
.joined(groupInfo), .invitationRemoved, .groupNotFound |
GMember |
Shared/Views/Chat/Group/ |
View-layer wrapper around GroupMember for list display |
Error Cases
| Error | Cause | Handling |
|---|---|---|
errorStore(.groupNotFound) |
Group deleted or not accessible | JoinGroupResult.groupNotFound |
errorAgent(.SMP(_, .AUTH)) |
Invitation revoked | JoinGroupResult.invitationRemoved |
errorStore(.groupLinkNotFound) |
No group link exists | apiGetGroupLink returns nil |
duplicateGroupLink |
Link already exists for group | Show alert |
errorAgent(.NOTICE(server, preset, expires)) |
Server notice during link creation | showClientNotice alert |
| Network errors | SMP/XFTP server unreachable | Retryable via chatApiSendCmdWithRetry |
Key Files
| File | Purpose |
|---|---|
Shared/Views/NewChat/AddGroupView.swift |
Group creation UI |
Shared/Views/Chat/Group/AddGroupMembersView.swift |
Member invitation UI |
Shared/Views/Chat/Group/GroupLinkView.swift |
Group link management UI |
Shared/Views/Chat/Group/GroupProfileView.swift |
Group profile editing |
Shared/Views/Chat/Group/GroupPreferencesView.swift |
Feature preferences UI |
Shared/Views/Chat/Group/GroupWelcomeView.swift |
Welcome message editing |
Shared/Views/Chat/Group/MemberAdmissionView.swift |
Admission settings UI |
Shared/Views/Chat/Group/MemberSupportView.swift |
Admin-to-pending-member chat |
Shared/Views/Chat/Group/MemberSupportChatToolbar.swift |
Support chat accept/reject toolbar |
Shared/Views/Chat/Group/SecondaryChatView.swift |
Secondary chat view for member support |
Shared/Model/SimpleXAPI.swift |
All group API functions |
Shared/Model/AppAPITypes.swift |
GroupLink, ConnectionPlan |
SimpleXChat/ChatTypes.swift |
GroupInfo, GroupProfile, GroupMember, GroupMemberRole |
Related Specifications
apps/ios/product/README.md-- Product overview: Groups capability mapapps/ios/product/flows/connection.md-- Connection flow (group links use the same connect mechanism)apps/ios/product/flows/messaging.md-- Messaging within groups