krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

0aa8cdf3bb55f31039df1ce473dc23eca22b6449

unsigned

author: Christian Cleberg <hello@cleberg.net> · 2026-07-24T22:51:57Z

fix: reach CloudKit instead of gating on unreliable entitlement introspection (#29)

iCloud sync always failed with "This build does not have the CloudKit
entitlement required for iCloud sync." on normal Debug/device builds.

`cloudKitContainer()` gated construction on `isEntitlementConfigurationAvailable()`,
which inspected `Info.plist` keys and an `archived-expanded-entitlements.xcent`
file. Entitlements live in the code signature, not `Info.plist`, and the `.xcent`
file only exists in archived/distribution builds — so the check returned false for
a correctly-signed development build, the container was never created, and every
path reported `.missingEntitlement`.

Stop introspecting entitlements. Always construct `CKContainer.default()` and let
CloudKit's own `accountStatus()` drive availability. When the entitlement is
genuinely absent, CloudKit surfaces `.missingEntitlement` / `.badContainer` /
`.permissionFailure`, which `accountStatus()` now maps back to the "no entitlement"
message — preserving that signal without the false negative.
 DomainDig/CloudSyncService.swift | 53 ++++++++++------------------------------
 1 file changed, 13 insertions(+), 40 deletions(-)

diff --git a/DomainDig/CloudSyncService.swift b/DomainDig/CloudSyncService.swift
index 985d2a4..7e12ee6 100644
--- a/DomainDig/CloudSyncService.swift
+++ b/DomainDig/CloudSyncService.swift
@@ -336,9 +336,7 @@ final class CloudSyncService {
     }
 
     func acceptShare(metadata: CKShare.Metadata) async throws {
-        guard let container = cloudKitContainer() else {
-            throw CloudSyncRuntimeError.missingEntitlement
-        }
+        let container = cloudKitContainer()
 
         if metadata.participantStatus == .pending {
             _ = try await container.accept(metadata)
@@ -1019,19 +1017,19 @@ final class CloudSyncService {
     }
 
     private func accountStatus() async -> AvailabilityState {
-        guard let container = cloudKitContainer() else {
-            return .missingEntitlement
-        }
+        let container = cloudKitContainer()
 
         return await withCheckedContinuation { continuation in
             container.accountStatus { status, error in
                 if let error {
-                    let ckError = error as? CKError
-                    if ckError?.code == .notAuthenticated {
+                    switch (error as? CKError)?.code {
+                    case .notAuthenticated:
                         continuation.resume(returning: AvailabilityState.noAccount)
-                        return
+                    case .missingEntitlement, .badContainer, .permissionFailure:
+                        continuation.resume(returning: AvailabilityState.missingEntitlement)
+                    default:
+                        continuation.resume(returning: AvailabilityState.unknown)
                     }
-                    continuation.resume(returning: AvailabilityState.unknown)
                     return
                 }
 
@@ -1385,18 +1383,14 @@ final class CloudSyncService {
             .lowercased() ?? ""
     }
 
-    private func cloudKitContainer() -> CKContainer? {
+    private func cloudKitContainer() -> CKContainer {
         if let container {
             return container
         }
 
-        if isEntitlementConfigurationAvailable() {
-            let container = CKContainer.default()
-            self.container = container
-            return container
-        }
-
-        return nil
+        let container = CKContainer.default()
+        self.container = container
+        return container
     }
 
     private func cloudKitDatabase(in scope: SyncDatabaseScope) -> CKDatabase? {
@@ -1411,9 +1405,7 @@ final class CloudSyncService {
             }
         }
 
-        guard let container = cloudKitContainer() else {
-            return nil
-        }
+        let container = cloudKitContainer()
 
         switch scope {
         case .privateDatabase:
@@ -1427,25 +1419,6 @@ final class CloudSyncService {
         }
     }
 
-    private func isEntitlementConfigurationAvailable() -> Bool {
-        if Bundle.main.object(forInfoDictionaryKey: "com.apple.developer.icloud-container-identifiers") != nil {
-            return true
-        }
-
-        if Bundle.main.object(forInfoDictionaryKey: "com.apple.developer.ubiquity-container-identifiers") != nil {
-            return true
-        }
-
-        if let entitlementsURL = Bundle.main.url(forResource: "archived-expanded-entitlements", withExtension: "xcent"),
-           let data = try? Data(contentsOf: entitlementsURL),
-           let contents = String(data: data, encoding: .utf8) {
-            return contents.contains("com.apple.developer.icloud-services")
-                && (contents.contains("CloudKit") || contents.contains("CloudKit-Anonymous"))
-        }
-
-        return false
-    }
-
     private var missingEntitlementMessage: String {
         "This build does not have the CloudKit entitlement required for iCloud sync."
     }