How Telegram-iOS Implements iCloud Sync and CloudKit for Account Data
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. 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:
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:
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.
Public Container Configuration
The implementation accesses CloudKit through the default public container:
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:
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:
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
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
Retrieving Emergency Datacenter Data via CloudKit
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
Summary
- iCloud Documents: Handles user-selected files through security-scoped bookmarks in
ICloudResources.swift, usingNSMetadataQueryfor 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.swiftwith 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:
CloudFileMediaResourcewrapsICloudFileResourcefor Telegram Core integration, andCloudDataContextprovides 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →