# How Telegram-iOS Implements iCloud Sync and CloudKit for Account Data

> Discover how Telegram-iOS uses iCloud Sync and CloudKit to securely store account data, ensuring seamless syncing and reliable account recovery through encrypted emergency datacenter information.

- Repository: [TelegramMessenger/Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS)
- Tags: internals
- Published: 2026-04-07

---

**Telegram-iOS leverages iCloud Documents to sync user-selected files through security-scoped bookmarks and CloudKit public records to store encrypted emergency datacenter data for account recovery when MTProto connections fail.**

The TelegramMessenger/Telegram-iOS repository contains sophisticated cloud integration logic that spans two distinct Apple frameworks. The implementation handles both document synchronization for media attachments and critical account backup functionality essential for restoring access when standard MTProto connections fail.

## iCloud Documents Sync for File Resources

Telegram-iOS manages user-selected files from iCloud Drive through a security-scoped resource system defined in [`submodules/ICloudResources/Sources/ICloudResources.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/submodules/ICloudResources/Sources/ICloudResources.swift). This module handles the complete lifecycle of file bookmarks, metadata queries, and thumbnail generation.

### Security-Scoped Bookmarks and URL Access

When a user selects a file from the iCloud Drive picker, the app receives a **bookmark** (`urlData`) that must be converted to a security-scoped URL. The `ICloudFileResource` class encapsulates this data:

```swift
public init(urlData: String, thumbnail: Bool) {
    // Initialize with bookmark data and thumbnail flag
}

public required init(decoder: PostboxDecoder) {
    // Decode from Postbox storage
}

```

The implementation resolves the bookmark using `URL(resolvingBookmarkData:)` and immediately calls `url.startAccessingSecurityScopedResource()` to grant the sandboxed app permission to read the file. This security-scoped access is critical for maintaining permissions across app restarts without requiring the user to re-select files.

### Handling Remote Files with NSMetadataQuery

The `fetchICloudFileResource(resource:)` function returns a **Signal** (Telegram’s reactive primitive) that manages both local and remotely stored files:

```swift
public func fetchICloudFileResource(resource: ICloudFileResource) 
    -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>

```

When a file resides only in iCloud (`isRemote == true` and `isCurrent == false`), the implementation launches an `NSMetadataQuery` to monitor the download status. The Signal emits the file data once the download completes, using the same read path as locally available files. This ensures seamless access regardless of the file’s current synchronization state.

### Thumbnail Generation

When `resource.thumbnail` is `true`, the system reads the file content, scales it to a maximum of 256 pixels, converts it to JPEG format, and returns it as a temporary file via `.moveTempFile`. The `iCloudFileDescription(_:)` helper function generates metadata including file size, name, and optional audio attributes by checking download status or executing a metadata query if needed.

## CloudKit Integration for Emergency Datacenter Backup

Telegram-iOS uses CloudKit public records to store **MTBackupDatacenterData**, enabling account restoration when normal MTProto connections fail. This implementation resides in [`submodules/CloudData/Sources/CloudData.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/submodules/CloudData/Sources/CloudData.swift).

### Public Container Configuration

The implementation accesses CloudKit through the default public container:

```swift
let container = CKContainer.default()
let publicDatabase = container.database(with: .public)

```

Using the public database allows the app to retrieve emergency datacenter information without requiring user authentication or Apple ID association, ensuring the backup remains accessible even when the user cannot authenticate through standard means.

### Record Naming and Phone Number Prefixing

The record name embeds a one-character prefix derived from the user’s phone number to distribute load and avoid single-record bottlenecks:

```swift
let recordName = "emergency-datacenter-\(prefix)"

```

This naming scheme allows Telegram to store a single record per phone number prefix, keeping individual payloads tiny while maintaining global availability.

### Data Fetching and Decoding

The `CloudDataContextImpl` class implements the `CloudDataContext` protocol, providing a throttled API that limits requests to once per minute per prefix via `CloudDataPrefixContext`:

```swift
public func get(phoneNumber: Signal<String?, NoError>) 
    -> Signal<MTBackupDatacenterData, NoError>

```

The fetch operation retrieves the record using `publicDatabase.fetch(withRecordID: recordId)`, extracts the base-64 encoded `"data"` field, and truncates the result to **256 bytes**. The implementation then decrypts this data using `MTIPDataDecode(encryptionProvider, data, phoneNumber)` to produce the `MTBackupDatacenterData` required for MTProto fallback connections. Network unavailable errors (CKErrorDomain code 1) are specifically caught and reported as `.networkUnavailable`, while other failures return `.generic`.

## Implementation Examples

The following examples demonstrate the practical usage of both cloud systems in Telegram-iOS.

### Reading an iCloud Document with Thumbnail Generation

```swift
let resource = ICloudFileResource(urlData: bookmarkString, thumbnail: true)

fetchICloudFileResource(resource: resource)
    .start(next: { result in
        switch result {
        case .moveTempFile(let file):
            // file.path contains the generated JPEG thumbnail
            print("Thumbnail stored at:", file.path)
        default:
            break
        }
    })

```

*Source*: [`submodules/ICloudResources/Sources/ICloudResources.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/submodules/ICloudResources/Sources/ICloudResources.swift)

### Retrieving Emergency Datacenter Data via CloudKit

```swift
let cloudData = CloudDataContextImpl(encryptionProvider: encryptionProvider)

cloudData.get(phoneNumber: .single("+1234567890"))
    .start(next: { backupData in
        // backupData is MTBackupDatacenterData for MTProto fallback
        print("Received backup datacenter:", backupData)
    })

```

*Source*: [`submodules/CloudData/Sources/CloudData.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/submodules/CloudData/Sources/CloudData.swift)

## Summary

- **iCloud Documents**: Handles user-selected files through security-scoped bookmarks in [`ICloudResources.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/ICloudResources.swift), using `NSMetadataQuery` for remote file monitoring and automatic thumbnail generation.
- **CloudKit Backup**: Stores encrypted emergency datacenter data in public CloudKit records named with phone number prefixes, implemented in [`CloudData.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/CloudData.swift) with request throttling and 256-byte payload limits.
- **Security Model**: iCloud implementation requires explicit security-scoped resource access, while CloudKit uses public database records accessible without authentication.
- **Integration Points**: `CloudFileMediaResource` wraps `ICloudFileResource` for Telegram Core integration, and `CloudDataContext` provides the public API for backup data retrieval.

## Frequently Asked Questions

### How does Telegram-iOS maintain access to iCloud files after app restart?

The implementation stores **security-scoped bookmarks** (`urlData`) in Postbox persistence. Upon retrieval, the app resolves the bookmark using `URL(resolvingBookmarkData:)` and immediately calls `startAccessingSecurityScopedResource()` to re-establish sandbox permissions. This bookmark-based approach persists across app launches without requiring the user to re-select files.

### Why does the CloudKit implementation limit data to 256 bytes?

The emergency datacenter record contains only the essential `MTBackupDatacenterData` required to establish a failover MTProto connection. The implementation explicitly truncates the decoded base-64 data to **256 bytes** because this small payload contains sufficient encrypted information to locate alternative datacenters while minimizing CloudKit storage overhead and fetch latency.

### How does the app handle network failures when fetching CloudKit records?

The [`CloudData.swift`](https://github.com/TelegramMessenger/Telegram-iOS/blob/main/CloudData.swift) implementation distinguishes between network-specific failures and generic errors. When `CKErrorDomain` code 1 (network unavailable) occurs, the system returns `.networkUnavailable`, allowing upstream logic to trigger appropriate retry mechanisms or offline fallbacks. All other errors return `.generic`, preventing infinite retry loops on permanent failures.

### What is the purpose of the phone number prefix in CloudKit record names?

The prefix (first character of the phone number) creates a **sharded record naming scheme** (`emergency-datacenter-\(prefix)`). This distribution prevents any single CloudKit record from becoming a global hotspot while still allowing efficient lookups. The `CloudDataPrefixContext` caches results per prefix and enforces a one-minute throttle to respect CloudKit rate limits.