From 0aa8cdf3bb55f31039df1ce473dc23eca22b6449 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Fri, 24 Jul 2026 17:51:57 -0500 Subject: fix: reach CloudKit instead of gating on unreliable entitlement introspection (#29) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(-) (limited to 'DomainDig/CloudSyncService.swift') 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." } -- cgit v1.2.3