Skip to main content
Enable the current group owner to delegate ownership to another member using CometChat’s UIKit for iOS.

Overview

The Transfer Group Ownership feature provides a modal interface where the group owner can:
  • See a list of current group members (excluding themselves)
  • Select exactly one member to become the new owner
  • Trigger the ownership transfer API call
  • Automatically exit the group after successful transfer

Prerequisites

Before implementing ownership transfer, ensure you have:
  • Completed Getting Started setup
  • CometChat UIKit v5+ installed via CocoaPods or Swift Package Manager
  • CometChatSDK and CometChatUIKitSwift integrated
  • User logged in with CometChatUIKit.login()
  • UINavigationController or modal presentation flow set up
  • Group context (GUID) available when invoking the transfer screen

Components

ComponentDescription
TransferOwnershipSubclass of CometChatGroupMembers enabling single selection mode
viewModel.groupMembersData source array of GroupMember objects
onSelectedItemProceedClosure invoked when user confirms selection
CometChat.transferGroupOwnershipAPI call to delegate group ownership
spinnerViewUIActivityIndicatorView showing loading state
leaveGroupCallbackCallback to perform group exit after transfer

Integration Steps

Step 1: Present Transfer Ownership Screen

Show the ownership transfer UI modally:
import UIKit
import CometChatSDK
import CometChatUIKitSwift

class GroupDetailsViewController: UIViewController {
    
    var group: Group?
    
    func showTransferOwnership(for group: Group) {
        // Initialize transfer ownership view controller
        let transferVC = TransferOwnership()
        transferVC.set(group: group)
        
        // Set callback for after transfer completes
        transferVC.leaveGroupCallback = { [weak self] in
            self?.leaveGroup()
        }
        
        // Present modally with navigation
        let nav = UINavigationController(rootViewController: transferVC)
        present(nav, animated: true)
    }
    
    private func leaveGroup() {
        // Handle leaving group after ownership transfer
        navigationController?.popToRootViewController(animated: true)
    }
}
File reference: GroupDetailsViewController.swift

Step 2: Configure Single Selection Mode

Restrict selection to one member and capture selection:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class TransferOwnership: CometChatGroupMembers {
    
    var leaveGroupCallback: (() -> Void)?
    
    private let spinnerView: UIActivityIndicatorView = {
        let spinner = UIActivityIndicatorView(style: .medium)
        spinner.hidesWhenStopped = true
        return spinner
    }()
    
    override func viewDidLoad() {
        // Enable single selection mode
        selectionMode = .single
        
        super.viewDidLoad()
        
        // Set navigation title
        title = "OWNERSHIP_TRANSFER".localize()
        
        // Handle member selection
        onSelectedItemProceed = { [weak self] users in
            if let newOwner = users.first as? GroupMember {
                self?.transferOwnership(to: newOwner)
            }
        }
    }
}
File reference: TransferOwnership.swift

Step 3: Load and Filter Member List

Exclude the current owner from the selectable list:
override func reloadData() {
    super.reloadData()
    
    viewModel.reload = { [weak self] in
        guard let self = self else { return }
        
        // Get current user's UID
        let currentUID = CometChat.getLoggedInUser()?.uid
        
        // Remove current owner from the list
        self.viewModel.groupMembers.removeAll { $0.uid == currentUID }
        
        DispatchQueue.main.async {
            // Update UI state
            self.removeLoadingView()
            self.removeErrorView()
            self.reload()
            
            // Show empty state if no eligible members
            if self.viewModel.groupMembers.isEmpty {
                self.showEmptyView()
            }
        }
    }
}
File reference: TransferOwnership.swift

Step 4: Perform Ownership Transfer

Call the API, emit event, and exit the group:
func transferOwnership(to member: GroupMember) {
    // Show loading indicator
    addSpinnerView()
    
    let uid = member.uid ?? ""
    let guid = viewModel.group.guid
    
    CometChat.transferGroupOwnership(UID: uid, GUID: guid) { [weak self] successMessage in
        DispatchQueue.main.async {
            guard let self = self else { return }
            
            // Update local group state
            self.viewModel.group.owner = uid
            
            // Emit ownership changed event for other components
            CometChatGroupEvents.ccOwnershipChanged(
                group: self.viewModel.group,
                newOwner: member
            )
            
            // Execute leave group callback
            self.leaveGroupCallback?()
            
            // Hide loading and dismiss
            self.removeSpinnerView()
            self.dismiss(animated: true)
        }
    } onError: { [weak self] error in
        DispatchQueue.main.async {
            self?.removeSpinnerView()
            
            // Show error alert
            let alert = UIAlertController(
                title: "Transfer Failed",
                message: error?.errorDescription ?? "Unable to transfer ownership",
                preferredStyle: .alert
            )
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            self?.present(alert, animated: true)
        }
    }
}
File reference: TransferOwnership.swift

Step 5: Manage Loading State

Provide visual feedback during network calls:
func addSpinnerView() {
    // Start spinner animation
    spinnerView.startAnimating()
    
    // Replace right bar button with spinner
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinnerView)
}

func removeSpinnerView() {
    // Stop spinner animation
    spinnerView.stopAnimating()
    
    // Remove spinner from navigation bar
    navigationItem.rightBarButtonItem = nil
}
File reference: TransferOwnership.swift

Complete Implementation

Here’s the complete TransferOwnership class:
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class TransferOwnership: CometChatGroupMembers {
    
    // MARK: - Properties
    var leaveGroupCallback: (() -> Void)?
    
    private let spinnerView: UIActivityIndicatorView = {
        let spinner = UIActivityIndicatorView(style: .medium)
        spinner.hidesWhenStopped = true
        return spinner
    }()
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        selectionMode = .single
        super.viewDidLoad()
        
        title = "Transfer Ownership"
        
        onSelectedItemProceed = { [weak self] users in
            if let newOwner = users.first as? GroupMember {
                self?.transferOwnership(to: newOwner)
            }
        }
    }
    
    // MARK: - Data Loading
    override func reloadData() {
        super.reloadData()
        
        viewModel.reload = { [weak self] in
            guard let self = self else { return }
            
            let currentUID = CometChat.getLoggedInUser()?.uid
            self.viewModel.groupMembers.removeAll { $0.uid == currentUID }
            
            DispatchQueue.main.async {
                self.removeLoadingView()
                self.removeErrorView()
                self.reload()
                
                if self.viewModel.groupMembers.isEmpty {
                    self.showEmptyView()
                }
            }
        }
    }
    
    // MARK: - Transfer Ownership
    func transferOwnership(to member: GroupMember) {
        addSpinnerView()
        
        let uid = member.uid ?? ""
        let guid = viewModel.group.guid
        
        CometChat.transferGroupOwnership(UID: uid, GUID: guid) { [weak self] _ in
            DispatchQueue.main.async {
                guard let self = self else { return }
                
                self.viewModel.group.owner = uid
                CometChatGroupEvents.ccOwnershipChanged(
                    group: self.viewModel.group,
                    newOwner: member
                )
                
                self.leaveGroupCallback?()
                self.removeSpinnerView()
                self.dismiss(animated: true)
            }
        } onError: { [weak self] error in
            DispatchQueue.main.async {
                self?.removeSpinnerView()
                self?.showError(error)
            }
        }
    }
    
    // MARK: - Loading State
    func addSpinnerView() {
        spinnerView.startAnimating()
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinnerView)
    }
    
    func removeSpinnerView() {
        spinnerView.stopAnimating()
        navigationItem.rightBarButtonItem = nil
    }
    
    // MARK: - Error Handling
    private func showError(_ error: CometChatException?) {
        let alert = UIAlertController(
            title: "Transfer Failed",
            message: error?.errorDescription ?? "Unable to transfer ownership",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Customization Options

Title Text

Replace localization key with custom string:
title = "Select New Owner"

Spinner Style

Adjust spinnerView.style and color using CometChatTheme:
spinnerView.style = .large
spinnerView.color = CometChatTheme.palette.primary

Error Handling

Customize error alerts in the onError closure:
let alert = UIAlertController(
    title: "Oops!",
    message: "Could not transfer ownership. Please try again.",
    preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Retry", style: .default) { [weak self] _ in
    self?.transferOwnership(to: member)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(alert, animated: true)

Edge Cases

ScenarioHandling
Empty member listShow an informative empty state when no eligible members exist
Network failuresDisable proceed button until connection restores
Blocked membersExclude or disable blocked users from selection

Error Handling

Error TypeSolution
Transfer failuresPresent UIAlertController with retry option
Unexpected statesEnsure removeSpinnerView() always executes in defer

Feature Matrix

FeatureMethod / ComponentFile(s)
Launch transfer flowshowTransferOwnership(for:)GroupDetailsViewController.swift
Single-member selectionselectionMode = .singleTransferOwnership.swift
Filter out current ownerreloadData() overrideTransferOwnership.swift
Execute API transferCometChat.transferGroupOwnership()TransferOwnership.swift
Show/hide loading indicatoraddSpinnerView(), removeSpinnerView()TransferOwnership.swift