> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Block/Unblock User

> Build CometChat iOS UI Kit user profile actions for messaging, audio/video calls, block and unblock, and delete conversation flows.

Build a user profile screen with avatar display, messaging, audio/video calls, and block/unblock functionality using CometChat UIKit.

## Prerequisites

Before implementing this feature, ensure you have:

* Completed [Getting Started](/ui-kit/ios/getting-started) setup
* CometChat UIKit v5+ installed
* User logged in with `CometChatUIKit.login()`
* Navigation controller configured

## Overview

The user details screen provides:

* **Profile Display** - Avatar, name, and online status
* **Messaging** - Start one-on-one chats
* **Calling** - Audio and video call buttons
* **Block/Unblock** - Manage user relationships
* **Delete Conversation** - Remove chat history

## Basic Implementation

Create a simple user details screen:

```swift lines theme={null}
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class UserDetailsViewController: UIViewController {
    
    private var user: User?
    
    convenience init(user: User) {
        self.init()
        self.user = user
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        setupUI()
    }
    
    private func setupUI() {
        guard let user = user else { return }
        
        // Avatar
        let avatar = CometChatAvatar()
        avatar.set(user: user)
        avatar.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(avatar)

        // Name label
        let nameLabel = UILabel()
        nameLabel.text = user.name
        nameLabel.font = .systemFont(ofSize: 24, weight: .bold)
        nameLabel.textAlignment = .center
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(nameLabel)
        
        // Status label
        let statusLabel = UILabel()
        statusLabel.text = user.status == .online ? "Online" : "Offline"
        statusLabel.textColor = user.status == .online ? .systemGreen : .secondaryLabel
        statusLabel.font = .systemFont(ofSize: 14)
        statusLabel.textAlignment = .center
        statusLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(statusLabel)
        
        // Message button
        let messageButton = UIButton(type: .system)
        messageButton.setTitle("Message", for: .normal)
        messageButton.setImage(UIImage(systemName: "message.fill"), for: .normal)
        messageButton.addTarget(self, action: #selector(openChat), for: .touchUpInside)
        messageButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(messageButton)
        
        // Block button
        let blockButton = UIButton(type: .system)
        blockButton.setTitle("Block User", for: .normal)
        blockButton.setTitleColor(.systemRed, for: .normal)
        blockButton.addTarget(self, action: #selector(blockUser), for: .touchUpInside)
        blockButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(blockButton)
        
        NSLayoutConstraint.activate([
            avatar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
            avatar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            avatar.widthAnchor.constraint(equalToConstant: 100),
            avatar.heightAnchor.constraint(equalToConstant: 100),
            
            nameLabel.topAnchor.constraint(equalTo: avatar.bottomAnchor, constant: 16),
            nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            
            statusLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 8),
            statusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            
            messageButton.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 32),
            messageButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            
            blockButton.topAnchor.constraint(equalTo: messageButton.bottomAnchor, constant: 24),
            blockButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
    
    @objc private func openChat() {
        guard let user = user else { return }
        let messages = CometChatMessages()
        messages.set(user: user)
        navigationController?.pushViewController(messages, animated: true)
    }
    
    @objc private func blockUser() {
        guard let user = user else { return }
        CometChat.blockUsers([user.uid ?? ""]) { [weak self] _ in
            DispatchQueue.main.async {
                self?.showAlert(title: "Blocked", message: "\(user.name ?? "User") has been blocked")
            }
        } onError: { error in
            print("Block error: \(error?.errorDescription ?? "")")
        }
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}
```

## Production Implementation

Complete user details screen with all features and event handling:

```swift lines theme={null}
import UIKit
import CometChatUIKitSwift
import CometChatSDK
#if canImport(CometChatCallsSDK)
import CometChatCallsSDK
#endif

class ProductionUserDetailsViewController: UIViewController {
    
    // MARK: - Properties
    private var user: User?
    private var isBlocked: Bool = false
    private let listenerID = "user-details-listener"
    
    // MARK: - UI Components
    private let scrollView = UIScrollView()
    private let contentView = UIView()
    private let avatar = CometChatAvatar()
    private let nameLabel = UILabel()
    private let statusLabel = UILabel()
    private let buttonStackView = UIStackView()
    private let messageButton = UIButton(type: .system)
    private let audioCallButton = UIButton(type: .system)
    private let videoCallButton = UIButton(type: .system)
    private let blockButton = UIButton(type: .system)
    private let deleteButton = UIButton(type: .system)
    
    // MARK: - Initialization
    convenience init(user: User) {
        self.init()
        self.user = user
    }
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        setupUI()
        configureWithUser()
        checkBlockStatus()
        addEventListeners()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setupNavigationBar()
    }
    
    deinit {
        removeEventListeners()
    }

    // MARK: - Setup
    private func setupNavigationBar() {
        title = "User Details"
        navigationController?.navigationBar.prefersLargeTitles = false
    }
    
    private func setupUI() {
        // Scroll View
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        
        contentView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(contentView)
        
        // Avatar
        avatar.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(avatar)
        
        // Name Label
        nameLabel.font = .systemFont(ofSize: 24, weight: .bold)
        nameLabel.textAlignment = .center
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(nameLabel)
        
        // Status Label
        statusLabel.font = .systemFont(ofSize: 14)
        statusLabel.textAlignment = .center
        statusLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(statusLabel)
        
        // Button Stack
        buttonStackView.axis = .horizontal
        buttonStackView.spacing = 24
        buttonStackView.distribution = .equalSpacing
        buttonStackView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(buttonStackView)
        
        // Message Button
        configureButton(messageButton, title: "Message", icon: "message.fill", action: #selector(openChat))
        buttonStackView.addArrangedSubview(messageButton)
        
        // Call Buttons (conditional)
        #if canImport(CometChatCallsSDK)
        configureButton(audioCallButton, title: "Audio", icon: "phone.fill", action: #selector(startAudioCall))
        configureButton(videoCallButton, title: "Video", icon: "video.fill", action: #selector(startVideoCall))
        buttonStackView.addArrangedSubview(audioCallButton)
        buttonStackView.addArrangedSubview(videoCallButton)
        #endif
        
        // Block Button
        blockButton.setTitle("Block User", for: .normal)
        blockButton.setTitleColor(.systemRed, for: .normal)
        blockButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
        blockButton.addTarget(self, action: #selector(toggleBlock), for: .touchUpInside)
        blockButton.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(blockButton)
        
        // Delete Button
        deleteButton.setTitle("Delete Conversation", for: .normal)
        deleteButton.setTitleColor(.systemRed, for: .normal)
        deleteButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
        deleteButton.addTarget(self, action: #selector(deleteConversation), for: .touchUpInside)
        deleteButton.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(deleteButton)
        
        setupConstraints()
    }
    
    private func configureButton(_ button: UIButton, title: String, icon: String, action: Selector) {
        button.setTitle(title, for: .normal)
        button.setImage(UIImage(systemName: icon), for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 12)
        button.tintColor = .systemBlue
        button.configuration = .plain()
        button.configuration?.imagePlacement = .top
        button.configuration?.imagePadding = 8
        button.addTarget(self, action: action, for: .touchUpInside)
    }
    
    private func setupConstraints() {
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            
            avatar.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 40),
            avatar.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            avatar.widthAnchor.constraint(equalToConstant: 100),
            avatar.heightAnchor.constraint(equalToConstant: 100),
            
            nameLabel.topAnchor.constraint(equalTo: avatar.bottomAnchor, constant: 16),
            nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            nameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            
            statusLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 8),
            statusLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            
            buttonStackView.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 32),
            buttonStackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            
            blockButton.topAnchor.constraint(equalTo: buttonStackView.bottomAnchor, constant: 48),
            blockButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            
            deleteButton.topAnchor.constraint(equalTo: blockButton.bottomAnchor, constant: 16),
            deleteButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            deleteButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40)
        ])
    }

    // MARK: - Configuration
    private func configureWithUser() {
        guard let user = user else { return }
        
        avatar.set(user: user)
        nameLabel.text = user.name
        updateStatusLabel(user: user)
    }
    
    private func updateStatusLabel(user: User) {
        if isBlocked {
            statusLabel.text = "Blocked"
            statusLabel.textColor = .systemRed
        } else {
            statusLabel.text = user.status == .online ? "Online" : "Offline"
            statusLabel.textColor = user.status == .online ? .systemGreen : .secondaryLabel
        }
    }
    
    private func checkBlockStatus() {
        guard let uid = user?.uid else { return }
        
        let request = BlockedUserRequest.BlockedUserRequestBuilder()
            .set(limit: 100)
            .build()
        
        request.fetchNext { [weak self] blockedUsers in
            let isBlocked = blockedUsers?.contains { $0.uid == uid } ?? false
            DispatchQueue.main.async {
                self?.isBlocked = isBlocked
                self?.updateBlockButton()
                self?.updateStatusLabel(user: self?.user ?? User())
            }
        } onError: { error in
            print("Error checking block status: \(error?.errorDescription ?? "")")
        }
    }
    
    private func updateBlockButton() {
        let title = isBlocked ? "Unblock User" : "Block User"
        blockButton.setTitle(title, for: .normal)
    }
    
    // MARK: - Actions
    @objc private func openChat() {
        guard let user = user else { return }
        
        if isBlocked {
            showAlert(title: "User Blocked", message: "Unblock this user to send messages")
            return
        }
        
        let messages = CometChatMessages()
        messages.set(user: user)
        navigationController?.pushViewController(messages, animated: true)
    }
    
    #if canImport(CometChatCallsSDK)
    @objc private func startAudioCall() {
        guard let user = user else { return }
        
        if isBlocked {
            showAlert(title: "User Blocked", message: "Unblock this user to make calls")
            return
        }
        
        let call = Call(receiverId: user.uid ?? "", callType: .audio, receiverType: .user)
        CometChat.initiateCall(call: call) { call in
            print("Audio call initiated: \(call?.sessionID ?? "")")
        } onError: { [weak self] error in
            self?.showAlert(title: "Call Failed", message: error?.errorDescription ?? "Unable to start call")
        }
    }
    
    @objc private func startVideoCall() {
        guard let user = user else { return }
        
        if isBlocked {
            showAlert(title: "User Blocked", message: "Unblock this user to make calls")
            return
        }
        
        let call = Call(receiverId: user.uid ?? "", callType: .video, receiverType: .user)
        CometChat.initiateCall(call: call) { call in
            print("Video call initiated: \(call?.sessionID ?? "")")
        } onError: { [weak self] error in
            self?.showAlert(title: "Call Failed", message: error?.errorDescription ?? "Unable to start call")
        }
    }
    #endif
    
    @objc private func toggleBlock() {
        if isBlocked {
            unblockUser()
        } else {
            showBlockConfirmation()
        }
    }
    
    private func showBlockConfirmation() {
        let alert = UIAlertController(
            title: "Block User",
            message: "Are you sure you want to block \(user?.name ?? "this user")? They won't be able to send you messages or call you.",
            preferredStyle: .alert
        )
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        alert.addAction(UIAlertAction(title: "Block", style: .destructive) { [weak self] _ in
            self?.blockUser()
        })
        
        present(alert, animated: true)
    }
    
    private func blockUser() {
        guard let uid = user?.uid else { return }
        
        CometChat.blockUsers([uid]) { [weak self] blockedUsers in
            DispatchQueue.main.async {
                self?.isBlocked = true
                self?.updateBlockButton()
                self?.updateStatusLabel(user: self?.user ?? User())
                
                // Emit event for other components
                if let user = self?.user {
                    CometChatUserEvents.ccUserBlocked(user: user)
                }
            }
        } onError: { [weak self] error in
            self?.showAlert(title: "Error", message: error?.errorDescription ?? "Failed to block user")
        }
    }
    
    private func unblockUser() {
        guard let uid = user?.uid else { return }
        
        CometChat.unblockUsers([uid]) { [weak self] unblockedUsers in
            DispatchQueue.main.async {
                self?.isBlocked = false
                self?.updateBlockButton()
                self?.updateStatusLabel(user: self?.user ?? User())
                
                // Emit event for other components
                if let user = self?.user {
                    CometChatUserEvents.ccUserUnblocked(user: user)
                }
            }
        } onError: { [weak self] error in
            self?.showAlert(title: "Error", message: error?.errorDescription ?? "Failed to unblock user")
        }
    }
    
    @objc private func deleteConversation() {
        let alert = UIAlertController(
            title: "Delete Conversation",
            message: "Are you sure you want to delete this conversation? This action cannot be undone.",
            preferredStyle: .alert
        )
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
            self?.performDeleteConversation()
        })
        
        present(alert, animated: true)
    }
    
    private func performDeleteConversation() {
        guard let uid = user?.uid else { return }
        
        CometChat.deleteConversation(conversationWith: uid, conversationType: .user) { [weak self] _ in
            DispatchQueue.main.async {
                self?.showAlert(title: "Deleted", message: "Conversation has been deleted")
            }
        } onError: { [weak self] error in
            self?.showAlert(title: "Error", message: error?.errorDescription ?? "Failed to delete conversation")
        }
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }

    // MARK: - Event Listeners
    private func addEventListeners() {
        CometChat.addUserListener(listenerID, self)
        CometChatUserEvents.addListener(listenerID, self)
    }
    
    private func removeEventListeners() {
        CometChat.removeUserListener(listenerID)
        CometChatUserEvents.removeListener(listenerID)
    }
}

// MARK: - CometChatUserDelegate
extension ProductionUserDetailsViewController: CometChatUserDelegate {
    
    func onUserOnline(user: User) {
        guard user.uid == self.user?.uid else { return }
        DispatchQueue.main.async {
            self.user = user
            self.updateStatusLabel(user: user)
        }
    }
    
    func onUserOffline(user: User) {
        guard user.uid == self.user?.uid else { return }
        DispatchQueue.main.async {
            self.user = user
            self.updateStatusLabel(user: user)
        }
    }
}

// MARK: - CometChatUserEventListener
extension ProductionUserDetailsViewController: CometChatUserEventListener {
    
    func ccUserBlocked(user: User) {
        guard user.uid == self.user?.uid else { return }
        DispatchQueue.main.async {
            self.isBlocked = true
            self.updateBlockButton()
            self.updateStatusLabel(user: user)
        }
    }
    
    func ccUserUnblocked(user: User) {
        guard user.uid == self.user?.uid else { return }
        DispatchQueue.main.async {
            self.isBlocked = false
            self.updateBlockButton()
            self.updateStatusLabel(user: user)
        }
    }
}
```

## Launching User Details

Open the user details screen from your app:

```swift lines theme={null}
import UIKit
import CometChatSDK

class UsersListViewController: UIViewController {
    
    func showUserDetails(for user: User) {
        let detailsVC = ProductionUserDetailsViewController(user: user)
        navigationController?.pushViewController(detailsVC, animated: true)
    }
    
    // From a users list
    func openFromUsersList() {
        let users = CometChatUsers()
        users.set(onItemClick: { [weak self] user, _ in
            self?.showUserDetails(for: user)
        })
        navigationController?.pushViewController(users, animated: true)
    }
    
    // From a conversation
    func openFromConversation(_ conversation: Conversation) {
        if let user = conversation.conversationWith as? User {
            showUserDetails(for: user)
        }
    }
}
```

## Block/Unblock API Reference

### Block Users

```swift lines theme={null}
// Block single user
CometChat.blockUsers(["user-uid"]) { blockedUsers in
    print("Blocked \(blockedUsers?.count ?? 0) users")
} onError: { error in
    print("Error: \(error?.errorDescription ?? "")")
}

// Block multiple users
CometChat.blockUsers(["user1", "user2", "user3"]) { blockedUsers in
    print("Blocked users: \(blockedUsers?.map { $0.uid ?? "" } ?? [])")
} onError: { error in
    print("Error: \(error?.errorDescription ?? "")")
}
```

### Unblock Users

```swift lines theme={null}
// Unblock single user
CometChat.unblockUsers(["user-uid"]) { unblockedUsers in
    print("Unblocked \(unblockedUsers?.count ?? 0) users")
} onError: { error in
    print("Error: \(error?.errorDescription ?? "")")
}

// Unblock multiple users
CometChat.unblockUsers(["user1", "user2"]) { unblockedUsers in
    print("Unblocked users: \(unblockedUsers?.map { $0.uid ?? "" } ?? [])")
} onError: { error in
    print("Error: \(error?.errorDescription ?? "")")
}
```

### Fetch Blocked Users

```swift lines theme={null}
let request = BlockedUserRequest.BlockedUserRequestBuilder()
    .set(limit: 50)
    .set(direction: .blockedByMe)  // or .hasBlockedMe, .both
    .build()

request.fetchNext { blockedUsers in
    for user in blockedUsers ?? [] {
        print("Blocked: \(user.name ?? "")")
    }
} onError: { error in
    print("Error: \(error?.errorDescription ?? "")")
}
```

## Styling

Customize the avatar appearance:

```swift lines theme={null}
let avatarStyle = AvatarStyle()
avatarStyle.cornerRadius = CometChatCornerStyle(cornerRadius: 50)
avatarStyle.borderWidth = 2
avatarStyle.borderColor = .systemBlue
avatarStyle.backgroundColor = .systemGray5

avatar.set(style: avatarStyle)
```

## Components Reference

| Component                        | Purpose                      |
| -------------------------------- | ---------------------------- |
| `CometChatAvatar`                | Display user profile picture |
| `CometChatMessages`              | Open chat interface          |
| `CometChat.blockUsers()`         | Block users                  |
| `CometChat.unblockUsers()`       | Unblock users                |
| `CometChat.deleteConversation()` | Delete chat history          |
| `CometChat.initiateCall()`       | Start audio/video calls      |

## Edge Cases

* **Self-blocking**: Hide block controls when viewing your own profile
* **Already blocked**: Check block status before showing chat/call options
* **Call SDK unavailable**: Conditionally show call buttons with `#if canImport(CometChatCallsSDK)`
* **Missing user data**: Show placeholder UI for incomplete profiles

## Error Handling

| Error                      | Solution                             |
| -------------------------- | ------------------------------------ |
| Block failed               | Show retry option, check network     |
| Unblock failed             | Verify user was previously blocked   |
| Call initiation failed     | Check permissions and call SDK setup |
| Delete conversation failed | Verify conversation exists           |

## Related Guides

<CardGroup cols={2}>
  <Card title="Users" icon="user" href="/ui-kit/ios/users">
    Display and manage user list
  </Card>

  <Card title="Events" icon="bell" href="/ui-kit/ios/events">
    Handle user and block events
  </Card>

  <Card title="Conversations" icon="comments" href="/ui-kit/ios/conversations">
    View and manage conversations
  </Card>
</CardGroup>

***

<CardGroup cols={2}>
  <Card title="Sample App" href="https://github.com/cometchat/cometchat-uikit-ios/tree/v5/SampleApp">
    Complete implementation example
  </Card>

  <Card title="UIKit Source" href="https://github.com/cometchat/cometchat-uikit-ios/tree/v5">
    CometChat UIKit iOS repository
  </Card>
</CardGroup>
