◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-06-16Advanced

Designing CloudKit Sync in a Rork Max Native App — Handling Conflicts and Deletes

You want the same data on iPhone and iPad. When you add CloudKit to a Swift app generated by Rork Max, the hard part is not saving — it is conflicts and deletes. Here are the design decisions I settled on.

Rork Max235CloudKitSwift48SyncArchitecture23

✦ Premium Article

Sync Is Not Done When It "Saves"

You add a favorite on iPhone, but it is missing on iPad. Or something you deleted on iPad comes back to the iPhone a while later. When I added sync to a native Swift app generated by Rork Max, this kind of inconsistency was the first thing I hit.

The code to save a record to CloudKit is straightforward. The hard part is the design: conflicts when several devices write at once, and how to propagate a delete to every device. Ship with that left vague and, to the user, it looks like "an app where data spontaneously appears and disappears." From my experience as an indie developer running apps over a long stretch, what erodes trust is not crashes so much as this kind of quiet inconsistency. Here are the decisions I locked in.

First, Draw the Line Between KV Store and CloudKit

iCloud sync offers several options. For small data like settings, NSUbiquitousKeyValueStore is enough — a lightweight mechanism that syncs key-value pairs only. Its capacity is around 1 MB, and it is not suited to a collection of structured records.

For data whose count grows — items the user creates like notes, favorites, collections — put it in CloudKit with CKRecord. My line: "fixed count addressable by key, use the KV store; variable count you will want to query, use CloudKit." Make that call up front, or you will later cram records into the KV store and break.

Saving a CloudKit record looks like this:

import CloudKit
 
struct FavoriteRecord {
    let id: CKRecord.ID
    var title: String
    var updatedAt: Date
}
 
func save(_ favorite: FavoriteRecord) async throws {
    let db = CKContainer.default().privateCloudDatabase
    let record = CKRecord(recordType: "Favorite", recordID: favorite.id)
    record["title"] = favorite.title
    record["updatedAt"] = favorite.updatedAt
    _ = try await db.save(record)
}

That part is easy. The trouble is what comes next.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Understand the boundary between NSUbiquitousKeyValueStore and CloudKit (CKRecord), and decide which to use
✦Learn the code to resolve conflicts when two devices edit the same record, using the server change token and changeTag
✦Reproduce the tombstone pattern that prevents 'zombie resurrection' when syncing deletes
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-04-05
Rork Max × WidgetKit & Live Activities in Practice — From Home Screen to Dynamic Island
A complete guide to implementing iOS Widgets, Lock Screen widgets, and Dynamic Island Live Activities with Rork Max. Covers WidgetKit fundamentals, Timeline update strategies, App Intents integration, and monetization.
⬡ Dev Tools2026-08-04
Exactly 20 Characters, Still Rejected: Putting Character Counting Behind One Boundary
Emoji and combining marks make your client and your server disagree about length. I measured four counting methods on Node v22 and found 75.6% of naive truncations split a grapheme and 36.1% produced replacement characters over UTF-8. Here is the shared-module design that fixed it.
⬡ Dev Tools2026-08-01
Every Experiment Kept Landing on the Same Devices: Hash Choice and Salt Position, Measured Across a Million IDs
On-device experiment assignment looked fine in isolation, then collapsed the moment two experiments ran side by side. Here is what one million IDs revealed about four hash functions, why the culprit was the concatenation order rather than hash quality, and the implementation I settled on, with the measured numbers.