@@ -50,9 +50,35 @@ actor CloudKitSyncEngine {
5050
5151 // MARK: - Push
5252
53+ /// CloudKit allows at most 400 items (saves + deletions) per modify operation
54+ private static let maxBatchSize = 400
55+
5356 func push( records: [ CKRecord ] , deletions: [ CKRecord . ID ] ) async throws {
5457 guard !records. isEmpty || !deletions. isEmpty else { return }
5558
59+ // Split into batches that fit within CloudKit's 400-item limit
60+ var remainingSaves = records [ ... ]
61+ var remainingDeletions = deletions [ ... ]
62+
63+ while !remainingSaves. isEmpty || !remainingDeletions. isEmpty {
64+ let batchSaves : [ CKRecord ]
65+ let batchDeletions : [ CKRecord . ID ]
66+
67+ let savesCount = min ( remainingSaves. count, Self . maxBatchSize)
68+ batchSaves = Array ( remainingSaves. prefix ( savesCount) )
69+ remainingSaves = remainingSaves. dropFirst ( savesCount)
70+
71+ let deletionsCount = min ( remainingDeletions. count, Self . maxBatchSize - savesCount)
72+ batchDeletions = Array ( remainingDeletions. prefix ( deletionsCount) )
73+ remainingDeletions = remainingDeletions. dropFirst ( deletionsCount)
74+
75+ try await pushBatch ( records: batchSaves, deletions: batchDeletions)
76+ }
77+
78+ Self . logger. info ( " Pushed \( records. count) records, \( deletions. count) deletions " )
79+ }
80+
81+ private func pushBatch( records: [ CKRecord ] , deletions: [ CKRecord . ID ] ) async throws {
5682 try await withRetry {
5783 let operation = CKModifyRecordsOperation (
5884 recordsToSave: records,
@@ -83,8 +109,6 @@ actor CloudKitSyncEngine {
83109 self . database. add ( operation)
84110 }
85111 }
86-
87- Self . logger. info ( " Pushed \( records. count) records, \( deletions. count) deletions " )
88112 }
89113
90114 // MARK: - Pull
0 commit comments