| name | cometchat-ios-production |
| description | Production-ready CometChat iOS setup — server-side auth tokens, security best practices, and deployment checklist. |
| license | MIT |
| compatibility | CometChatUIKitSwift ^5; iOS 13+ |
| metadata | {"author":"CometChat","version":"3.0.0","tags":"chat cometchat ios production auth tokens security deployment"} |
Ground truth: CometChatUIKitSwift ~> 5 (+ CometChatCallsSDK ~> 5) — Pods/SPM .swiftinterface + ui-kit/ios. Official docs: https://www.cometchat.com/docs/fundamentals/user-auth · Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP). Verify symbols against the installed package/source before relying on them.
Purpose
This skill teaches how to prepare your CometChat iOS integration for production. It covers replacing development Auth Keys with server-side auth tokens, security best practices, and a deployment checklist.
1. Development vs Production Authentication
Development Mode (Auth Key)
In development, you use the Auth Key directly in your app:
CometChatUIKit.login(uid: "user-123") { result in
}
Problems with Auth Key in production:
- Auth Key is embedded in your app binary
- Anyone can decompile your app and extract it
- Attackers can impersonate any user
- No server-side validation of user identity
Production Mode (Auth Token)
In production, your server generates short-lived auth tokens:
┌─────────────┐ 1. Login ┌─────────────┐
│ iOS App │ ───────────────► │ Your Server │
└─────────────┘ └─────────────┘
│ │
│ │ 2. Verify user
│ │ Generate token
│ ▼
│ ┌─────────────┐
│ │ CometChat │
│ │ API │
│ └─────────────┘
│ │
│ 3. Return auth token │
│ ◄──────────────────────────────┘
│
│ 4. Login with token
▼
┌─────────────┐
│ CometChat │
│ SDK │
└─────────────┘
2. Server-Side Token Generation
Your Server Endpoint
Create an endpoint that:
- Authenticates the user (your existing auth system)
- Calls CometChat API to generate an auth token
- Returns the token to the iOS app
Example (Node.js/Express):
const express = require('express');
const axios = require('axios');
const app = express();
const COMETCHAT_APP_ID = process.env.COMETCHAT_APP_ID;
const COMETCHAT_API_KEY = process.env.COMETCHAT_API_KEY;
const COMETCHAT_REGION = process.env.COMETCHAT_REGION;
app.post('/api/cometchat/token', async (req, res) => {
try {
const userId = req.user.id;
if (!userId) {
return res.status(401).json({ error: 'Unauthorized' });
}
const response = await axios.post(
`https://${COMETCHAT_APP_ID}.api-${COMETCHAT_REGION}.cometchat.io/v3/users/${userId}/auth_tokens`,
{},
{
headers: {
'apiKey': ,
:
}
}
);
res.({
: response...
});
} (error) {
.(, error.?. || error.);
res.().({ : });
}
});
Example (Python/Flask):
from flask import Flask, jsonify, request
import requests
import os
app = Flask(__name__)
COMETCHAT_APP_ID = os.environ.get('COMETCHAT_APP_ID')
COMETCHAT_API_KEY = os.environ.get('COMETCHAT_API_KEY')
COMETCHAT_REGION = os.environ.get('COMETCHAT_REGION')
@app.route('/api/cometchat/token', methods=['POST'])
def get_cometchat_token():
user_id = request.user.id
if not user_id:
return jsonify({'error': 'Unauthorized'}), 401
url = f'https://{COMETCHAT_APP_ID}.api-{COMETCHAT_REGION}.cometchat.io/v3/users/{user_id}/auth_tokens'
headers = {
'apiKey': COMETCHAT_API_KEY,
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers)
if response.status_code == 200:
data = response.json()
return jsonify({'authToken': data['data']['authToken']})
else:
return jsonify({'error': 'Failed to generate token'}), 500
CometChat REST API Reference
Create Auth Token:
POST https://{appId}.api-{region}.cometchat.io/v3/users/{uid}/auth_tokens
Headers:
apiKey: YOUR_REST_API_KEY
Content-Type: application/json
Response:
{
"data": {
"uid": "user-123",
"authToken": "user-123_abc123xyz..."
}
}
Create User (if needed):
POST https://{appId}.api-{region}.cometchat.io/v3/users
Headers:
apiKey: YOUR_REST_API_KEY
Content-Type: application/json
Body:
{
"uid": "user-123",
"name": "John Doe",
"avatar": "https://example.com/avatar.jpg"
}
3. iOS Implementation
CometChatManager for Production
import Foundation
import CometChatUIKitSwift
import CometChatSDK
final class CometChatManager {
static let shared = CometChatManager()
private(set) var isInitialized = false
private(set) var currentUser: User?
private init() {}
func initialize(completion: @escaping (Result<Bool, Error>) -> Void) {
guard !isInitialized else {
completion(.success(true))
return
}
let uiKitSettings = UIKitSettings()
.set(appID: AppConfig.cometChatAppID)
.set(region: AppConfig.cometChatRegion)
.subscribePresenceForAllUsers()
.build()
CometChatUIKit(uiKitSettings: uiKitSettings) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let success):
.isInitialized success
.currentUser .getLoggedInUser()
completion(.success(success))
.failure( error):
completion(.failure(error))
}
}
}
}
(: (<, >) -> ) {
isInitialized {
completion(.failure(.notInitialized))
}
user .getLoggedInUser() {
currentUser user
completion(.success(user))
}
fetchAuthToken { [ ] result
result {
.success( authToken):
.loginWithToken(authToken, completion: completion)
.failure( error):
completion(.failure(error))
}
}
}
(: (<, >) -> ) {
url (string: ) {
completion(.failure(.invalidURL))
}
request (url: url)
request.httpMethod
request.setValue(, forHTTPHeaderField: )
authToken .shared.accessToken {
request.setValue(, forHTTPHeaderField: )
}
.shared.dataTask(with: request) { data, response, error
error error {
completion(.failure(error))
}
data data {
completion(.failure(.noData))
}
{
json .jsonObject(with: data) [: ]
authToken json[] {
completion(.success(authToken))
} {
completion(.failure(.invalidResponse))
}
} {
completion(.failure(error))
}
}.resume()
}
( : , : (<, >) -> ) {
.login(authToken: authToken) { [ ] result
.main.async {
result {
.success( user):
.currentUser user
completion(.success(user))
.onError( error):
completion(.failure(.sdk(error)))
}
}
}
}
(: (<, >) -> ) {
user currentUser {
completion(.success(()))
}
.logout(user: user) { [ ] result
.main.async {
result {
.success:
.currentUser
completion(.success(()))
.onError( error):
completion(.failure(.sdk(error)))
}
}
}
}
}
: {
notInitialized
invalidURL
noData
invalidResponse
sdk()
errorDescription: ? {
{
.notInitialized:
.invalidURL:
.noData:
.invalidResponse:
.sdk( exception):
exception.errorDescription
}
}
}
App Configuration
import Foundation
struct AppConfig {
static let cometChatAppID: String = {
guard let appID = Bundle.main.object(forInfoDictionaryKey: "CometChatAppID") as? String else {
fatalError("CometChatAppID not found in Info.plist")
}
return appID
}()
static let cometChatRegion: String = {
guard let region = Bundle.main.object(forInfoDictionaryKey: "CometChatRegion") as? String else {
fatalError("CometChatRegion not found in Info.plist")
}
return region
}()
static let apiBaseURL: String = {
#if DEBUG
return "https://api-staging.yourapp.com"
#else
return "https://api.yourapp.com"
#endif
}()
}
Info.plist Configuration
<key>CometChatAppID</key>
<string>$(COMETCHAT_APP_ID)</string>
<key>CometChatRegion</key>
<string>$(COMETCHAT_REGION)</string>
xcconfig Files
Debug.xcconfig:
COMETCHAT_APP_ID = your_app_id
COMETCHAT_REGION = us
Release.xcconfig:
COMETCHAT_APP_ID = your_app_id
COMETCHAT_REGION = us
4. User Provisioning
Create Users on Your Server
When a user signs up in your app, create them in CometChat:
app.post('/api/register', async (req, res) => {
const { email, password, name } = req.body;
const user = await createUserInDatabase({ email, password, name });
await axios.post(
`https://${COMETCHAT_APP_ID}.api-${COMETCHAT_REGION}.cometchat.io/v3/users`,
{
uid: user.id,
name: user.name,
avatar: user.avatarUrl
},
{
headers: {
'apiKey': COMETCHAT_API_KEY,
'Content-Type': 'application/json'
}
}
);
res.json({ success: true, userId: user.id });
});
Update User Profile
When user updates their profile:
app.put('/api/profile', async (req, res) => {
const { name, avatar } = req.body;
const userId = req.user.id;
await updateUserInDatabase(userId, { name, avatar });
await axios.put(
`https://${COMETCHAT_APP_ID}.api-${COMETCHAT_REGION}.cometchat.io/v3/users/${userId}`,
{ name, avatar },
{
headers: {
'apiKey': COMETCHAT_API_KEY,
'Content-Type': 'application/json'
}
}
);
res.json({ success: true });
});
5. Security Best Practices
Never Expose API Keys
❌ Wrong:
let apiKey = "abc123xyz"
✅ Correct:
Validate User Identity
Always verify user identity on your server before generating tokens:
app.post('/api/cometchat/token', authenticateMiddleware, async (req, res) => {
const userId = req.user.id;
});
Use HTTPS
Always use HTTPS for API communication:
let url = URL(string: "https://api.yourapp.com/api/cometchat/token")
let url = URL(string: "http://api.yourapp.com/api/cometchat/token")
Token Expiration
Auth tokens have a default expiration. Handle token refresh:
func handleTokenExpired() {
CometChatManager.shared.logout { _ in
CometChatManager.shared.login { result in
switch result {
case .success:
print("Re-authenticated successfully")
case .failure(let error):
print("Re-authentication failed: \(error)")
}
}
}
}
App Transport Security
Ensure ATS is properly configured in Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
6. Error Handling
Handle Authentication Errors
func handleCometChatError(_ error: Error) {
if let cometChatError = error as? CometChatException {
switch cometChatError.errorCode {
case "ERR_UID_NOT_FOUND":
createUserAndRetry()
case "AUTH_ERR_AUTH_TOKEN_NOT_FOUND":
refreshTokenAndRetry()
case "ERR_NOT_LOGGED_IN":
navigateToLogin()
default:
showError(cometChatError.errorDescription ?? "Unknown error")
}
}
}
Retry Logic
func loginWithRetry(maxAttempts: Int = 3, completion: @escaping (Result<User, Error>) -> Void) {
var attempts = 0
func attempt() {
attempts += 1
CometChatManager.shared.login { result in
switch result {
case .success(let user):
completion(.success(user))
case .failure(let error):
if attempts < maxAttempts {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
attempt()
}
} else {
completion(.failure(error))
}
}
}
}
attempt()
}
7. Deployment Checklist
Before Submitting to App Store
CometChat Dashboard Configuration
Server Configuration
8. Monitoring and Analytics
Track CometChat Events
CometChat.addConnectionListener("connection-listener", self)
extension YourClass: CometChatConnectionDelegate {
func connected() {
Analytics.track("cometchat_connected")
}
func connecting() {
Analytics.track("cometchat_connecting")
}
func disconnected() {
Analytics.track("cometchat_disconnected")
}
}
Track Message Events
class AnalyticsListener: CometChatMessageEventListener {
func ccMessageSent(message: BaseMessage, status: MessageStatus) {
if status == .success {
Analytics.track("message_sent", properties: [
"type": message.messageType.rawValue,
"receiver_type": message.receiverType.rawValue
])
}
}
}
CometChatMessageEvents.addListener("analytics", AnalyticsListener())
9. Common Production Issues
| Issue | Cause | Solution |
|---|
| "User not found" | User not created in CometChat | Create user via REST API before login |
| "Invalid auth token" | Token expired or malformed | Generate new token from server |
| "Rate limit exceeded" | Too many API calls | Implement caching and rate limiting |
| Push not working | Certificate mismatch | Verify APNs cert matches environment |
| Calls failing | Missing SDK or permissions | Add CometChatCallsSDK and permissions |
Summary
Development → Production Migration:
- Remove Auth Key from iOS app
- Create server endpoint for token generation
- Update iOS app to fetch tokens from your server
- Create users in CometChat when they register
- Test thoroughly before release
- Monitor and handle errors gracefully