Network settings
Network settings for connecting to the server are described by the ChatNetworkConfig model.
Initialization:
The default constructor creates a model with the standard settings:
var chatNetworkConfig = ChatNetworkConfig()
You can configure individual connection components:
httpConfig: REST API settingswsConfig: WebSocket API settingssslPinning: SSL pinning settings
ChatNetworkConfig is a struct and is passed inside ChatConfig by value. After ChatCenterUISDK(chatConfig:) is called, the SDK works with its own copy; changes to chatConfig.networkConfig or its nested fields from the integrator's side have no effect. Set all fields before passing ChatConfig to the SDK constructor. Details — SDK settings.
REST API settings (HTTPConfig)
HTTP(S) connection parameters for the REST API:
| Field | Type | Required | Description |
|---|---|---|---|
connectionTimeout | TimeInterval (sec) | No (default 30) | Idle timeout for a REST request: applied as URLRequest.timeoutInterval and covers all phases (establishing the connection + waiting for a response). Applies only to REST; for WebSocket see WSConfig.connectionTimeout |
downloadTimeout | TimeInterval (sec) | No (default 30) | Idle timeout for a file download request: applied as URLRequest.timeoutInterval (same semantics as connectionTimeout) |
uploadTimeout | TimeInterval (sec) | No (default 120) | Applied simultaneously as URLSessionConfiguration.timeoutIntervalForRequest (idle timeout) and timeoutIntervalForResource (overall resource load limit). This means: for the default 120, the total file upload time must not exceed 120 seconds — for large files over a slow connection, the value needs to be increased. The SDK's upload session is also hard-configured with waitsForConnectivity = true: if there is no network, the request waits for it to be restored rather than failing immediately (REST and download, in contrast, fail right away). The total wait time is still limited by uploadTimeout |
WebSocket connection settings (WSConfig)
WS(S) connection parameters for real-time messages:
| Field | Type | Required | Description |
|---|---|---|---|
connectionTimeout | TimeInterval (sec) | No (default 30) | Timeout for the WebSocket upgrade request: the value is applied both as URLSessionConfiguration.timeoutIntervalForRequest and as the upgrade request's URLRequest.timeoutInterval |
sendTimeout | TimeInterval (sec) | No (default 20) | Used simultaneously as: (1) the interval of the timer that checks the status of unsent messages (the timer works while the chat screen is open and the dialog is active; on going to background it stops); (2) the threshold after which a message in the .sending status is marked as .failed if there is no server response. If the server returned an error response over WebSocket, the message becomes .failed immediately, bypassing the timer. The "no later than after 2 × sendTimeout" guarantee applies only to the timer path |
SSL pinning settings
When using this feature, it is not recommended to install only one trusted certificate. If it is revoked or expires, the SDK will stop connecting to the server. Use additional backup certificates and update them in time.
SSLPinningConfig parameters:
| Field | Type | Required | Description |
|---|---|---|---|
allowUntrustedSSLCertificate | Bool | No (default false) | When true, the SDK accepts any server certificate without signature verification. Unsafe for production — see the :::danger below. Ignored if trustedCertificates is non-empty — pinning has priority |
trustedCertificates | [ChatSSLCertificate] | No (default []) | A list of trusted certificates in DER format. When set, the SDK fully bypasses SecTrustEvaluateWithError — hostname, validity period, and revocation checks (OCSP/CRL) are ignored. The only check is whether the public key of any certificate in the server's TLS chain (leaf, intermediate, or root) matches one of the keys in the list (public key pinning). This allows you to pin a leaf certificate as well as an intermediate/root CA — the latter survives leaf rotation without an app update |
allowUntrustedSSLCertificate = true only takes effect when trustedCertificates is empty — in that case the SDK accepts any server certificate, which makes the application vulnerable to man-in-the-middle (MITM) attacks. With a non-empty trustedCertificates, the flag is ignored (pinning has priority), so there is no MITM exposure through it. Nevertheless, do not leave allowUntrustedSSLCertificate = true in production builds: if trustedCertificates turns out to be empty in some build configuration (for example, files were not found through Bundle.main.url(forResource:)), the app loses its protection without any explicit signal.
trustedCertificates replaces system TLS validation with public key verification — this works with regular CA-signed certificates, with self-signed certificates, and with certificates whose validity period has expired.
A certificate is passed as a ChatSSLCertificate model, in which a local path to the certificate is specified:
var chatNetworkConfig = ChatNetworkConfig()
if let mainCert = Bundle.main.url(forResource: "main_cert", withExtension: "cer"),
let backupCert = Bundle.main.url(forResource: "backup_cert", withExtension: "cer") {
chatNetworkConfig.sslPinning.trustedCertificates = [ChatSSLCertificate(contentsOf: mainCert), ChatSSLCertificate(contentsOf: backupCert)]
}
Certificate format
ChatSSLCertificate(contentsOf:) expects DER-encoded content — the SDK passes the file bytes directly to SecCertificateCreateWithData; the extension is not checked (.cer or .der are traditionally used, but .crt or a file without an extension will also work if the content is valid). PEM certificates (-----BEGIN CERTIFICATE-----…) are not supported — convert them before adding to the bundle:
openssl x509 -in cert.pem -outform DER -out cert.cer
Add the resulting .cer to the app bundle (Build Phases → Copy Bundle Resources). Loading via Bundle.main.url(forResource:withExtension:) will return nil if the file is missing — as in the example above, handle this explicitly: a silently missing certificate is equivalent to disabling pinning.
If the file at contentsOf: exists but is not a valid DER (for example, a PEM was placed in the bundle by mistake, or the file is corrupted), ChatSSLCertificate will store an internal certificate = nil and will not throw from the constructor. The SDK writes an error message to its log stream (visible with ChatLoggerConfig(logLevel: .all) — see Logging settings), but there is no failure signal in the integrator's code.
The effect differs from the "empty trustedCertificates array" case: an array with a corrupted ChatSSLCertificate is not empty, so the SDK still enters pinning mode. Corrupted elements are skipped by the loop and do not block valid ones: if there is at least one valid ChatSSLCertificate in the array with a matching public key, the connection will be established. But if all elements are corrupted — the SDK finds no matches and rejects all TLS connections. Externally this looks like "the network is broken," not "pinning is disabled."
After adding a certificate, verify both that the connection actually establishes and that swapping the server for a known "wrong" certificate breaks the connection.
SSL error diagnostics
If the connection does not open after enabling pinning:
| Symptom | Possible cause | What to check |
|---|---|---|
Connection aborts during handshake (SDK log shows SSL CERTIFICATES Pinning error - server and pinned certificates are not equals) | No public key from the server's TLS chain matches the public keys in trustedCertificates | Enable ChatLoggerConfig(logLevel: .all) and compare the hex values of SSL Server public key: (all certificates in the server chain) and SSL Pinned certificate public key: (those attached) — at least one pair must match. The SDK logs keys in raw format (SecKeyCopyExternalRepresentation: PKCS #1 for RSA, X9.63 for EC), so third-party commands such as openssl x509 -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 will produce a different hash (SubjectPublicKeyInfo) — they are not suitable for comparing with the SDK log |
| The connection worked, then stopped (without an app update) | The server certificate was rotated, and the app does not have the new one | Contact edna support; a temporary fallback is the backup certificate, if it was added in advance |
nil after Bundle.main.url(forResource:) | The file did not get into Copy Bundle Resources, or the name and extension were split incorrectly (forResource: "cert.cer" instead of forResource: "cert", withExtension: "cer") | Check target → Build Phases → Copy Bundle Resources (the file must be in the list); check that in Bundle.main.url(forResource:withExtension:) the file name is passed without the extension and the extension is the second argument |
| The error appears only in the Release build | The ATS configuration for the domain differs between Debug and Release (for example, NSAllowsArbitraryLoads: true is set only in Debug) | Check NSAppTransportSecurity in Info.plist of both builds; with correct server hosts (HTTPS with valid certificates), no special ATS settings are required |
Enable detailed SDK logging (ChatLoggerConfig(logLevel: .all)) — SSL handshake messages are written to the common log stream and are delivered to the ChatCenterUISDKDelegate delegate via the chatCenterUI(chatCenter:didLog:) method.
Certificate rotation
The server certificate has a validity period. To avoid an app blackout during rotation:
- Request the schedule from edna support — how long before expiration you will receive the new certificate.
- Always keep at least 2 certificates in
trustedCertificates: the current one + the backup (next). When edna rotates the certificate, the new one will already be in the app's trust list. - When rolling out an app version, make sure the new certificate is included in the bundle before the old one stops working on the server.
- Do not use
allowUntrustedSSLCertificate = truein production even temporarily as a workaround — that defeats the MITM protection for the entire duration of the fix.
Related
- Transport settings — server URLs
- SDK settings — chat feature parameters
- SDK initialization and setup — full initialization example
- Troubleshooting — diagnostics of connection problems