From 74f2b5f71f98ca2f8acdced9b7f7ddfefd6e4bfd Mon Sep 17 00:00:00 2001 From: Mojtaba Hosseini Date: Sat, 22 Aug 2026 01:21:40 +0330 Subject: [PATCH 1/4] feat: expose recoverable sharing errors --- .../SQLiteData/CloudKit/CloudKitSharing.swift | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift index 38cd2f17..cbd7e0be 100644 --- a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift +++ b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift @@ -33,7 +33,33 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine { - private struct SharingError: LocalizedError { + public enum RecoveryAction { + case sendChangesAndRetry + case startAndRetry + } + + public protocol RecoverableSharingError: LocalizedError { + var recoveryAction: RecoveryAction? { get } + } + } + + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + extension SyncEngine.SharingError: SyncEngine.RecoverableSharingError { + var recoveryAction: SyncEngine.RecoveryAction? { + switch reason { + case .shareCouldNotBeCreated: nil + case .recordMetadataNotFound: .sendChangesAndRetry + case .recordNotRoot: nil + case .recordTableNotSynchronized: nil + case .recordTablePrivate: nil + case .syncEngineNotRunning: .startAndRetry + } + } + } + + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + extension SyncEngine { + fileprivate struct SharingError: LocalizedError { enum Reason { case shareCouldNotBeCreated case recordMetadataNotFound From 9812cae077cbfdc3473ede669e834288e3803883 Mon Sep 17 00:00:00 2001 From: Mojtaba Hosseini Date: Sat, 22 Aug 2026 01:22:09 +0330 Subject: [PATCH 2/4] docs: document the recoverable sharing errors --- Sources/SQLiteData/CloudKit/CloudKitSharing.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift index cbd7e0be..71d62f87 100644 --- a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift +++ b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift @@ -33,12 +33,27 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine { + /// Describes an action that can be performed by the caller to recover from + /// an error and retry the failed operation. public enum RecoveryAction { + /// No sync metadata found for record. Has the record been saved to the database + /// and synchronized to iCloud? Invoke 'SyncEngine.sendChanges()' to force + /// synchronization. case sendChangesAndRetry + /// Sync engine is not running. Make sure engine is running by invoking the 'start()' + /// method, or using the 'startImmediately' argument when initializing the engine. case startAndRetry } + /// A protocol for errors that provide a system-defined recovery action. + /// + /// A conforming error may provide a recovery action when the failed + /// operation can potentially succeed after performing an additional + /// synchronization operation. public protocol RecoverableSharingError: LocalizedError { + /// The recovery action that can be performed before retrying the + /// failed operation, or `nil` when no automatic recovery action + /// is available. var recoveryAction: RecoveryAction? { get } } } From 601c8b5fa02adfe3a2570228162a815ea1b2ceb3 Mon Sep 17 00:00:00 2001 From: Mojtaba Hosseini Date: Sat, 22 Aug 2026 01:34:56 +0330 Subject: [PATCH 3/4] test: exposed sharing error recovery actions --- .../CloudKitTests/SharingTests.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift index 9d6705f4..0cf0a54e 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift @@ -82,6 +82,13 @@ ) """# } + + let recoverable = try #require(error as? any SyncEngine.RecoverableSharingError) + assertInlineSnapshot(of: recoverable.recoveryAction, as: .customDump) { + """ + SyncEngine.RecoveryAction.startAndRetry + """ + } } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @@ -316,6 +323,13 @@ ) """# } + + let recoverable = try #require(error as? any SyncEngine.RecoverableSharingError) + assertInlineSnapshot(of: recoverable.recoveryAction, as: .customDump) { + """ + SyncEngine.RecoveryAction.sendChangesAndRetry + """ + } } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) From 61c2d68947f9b50db3d5c606e584852434d291ed Mon Sep 17 00:00:00 2001 From: Mojtaba Hosseini Date: Sat, 22 Aug 2026 01:39:04 +0330 Subject: [PATCH 4/4] refactor: make sharing error recovery action type explicit --- Sources/SQLiteData/CloudKit/CloudKitSharing.swift | 6 +++--- Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift index 71d62f87..38afcc6b 100644 --- a/Sources/SQLiteData/CloudKit/CloudKitSharing.swift +++ b/Sources/SQLiteData/CloudKit/CloudKitSharing.swift @@ -35,7 +35,7 @@ extension SyncEngine { /// Describes an action that can be performed by the caller to recover from /// an error and retry the failed operation. - public enum RecoveryAction { + public enum SharingErrorRecoveryAction { /// No sync metadata found for record. Has the record been saved to the database /// and synchronized to iCloud? Invoke 'SyncEngine.sendChanges()' to force /// synchronization. @@ -54,13 +54,13 @@ /// The recovery action that can be performed before retrying the /// failed operation, or `nil` when no automatic recovery action /// is available. - var recoveryAction: RecoveryAction? { get } + var recoveryAction: SharingErrorRecoveryAction? { get } } } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension SyncEngine.SharingError: SyncEngine.RecoverableSharingError { - var recoveryAction: SyncEngine.RecoveryAction? { + var recoveryAction: SyncEngine.SharingErrorRecoveryAction? { switch reason { case .shareCouldNotBeCreated: nil case .recordMetadataNotFound: .sendChangesAndRetry diff --git a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift index 0cf0a54e..13df74cb 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift @@ -86,7 +86,7 @@ let recoverable = try #require(error as? any SyncEngine.RecoverableSharingError) assertInlineSnapshot(of: recoverable.recoveryAction, as: .customDump) { """ - SyncEngine.RecoveryAction.startAndRetry + SyncEngine.SharingErrorRecoveryAction.startAndRetry """ } } @@ -327,7 +327,7 @@ let recoverable = try #require(error as? any SyncEngine.RecoverableSharingError) assertInlineSnapshot(of: recoverable.recoveryAction, as: .customDump) { """ - SyncEngine.RecoveryAction.sendChangesAndRetry + SyncEngine.SharingErrorRecoveryAction.sendChangesAndRetry """ } }