Sync SwiftData with iCloud using CloudKit

Jakir Hossain
4 min readOct 19, 2023

If you know how to persist data using SwiftData or Core Data, you can easily sync those data to multiple devices using iCloud. If you use the Note app on any Apple device, you will be able to understand. If we add a new item in Note, it will automatically synchronize across all devices under an Apple ID.

CloudKit is Apple’s remote data storage service like Firebase or AWS Amplify. It provides an option to store and share app data using user’s iCloud accounts as a back-end storage service.

If you want to enable CloudKit for your app, you need to enroll in the Apple Developer program. If you have one, you are good to go. We are going to use SwiftData as local storage for this tutorial. If you are new to SwiftData, you can read this article: A Beginner’s Guide to SwiftData with a to-do app.

When you create a project in Xcode, you can select SwiftData as Storage and check Host in CloudKit.

This will generate a sample code for you. Make sure you have selected your developer Team from the Team option. You can also add Team or any of these options later from the app’s Signing & Capability.

Now from Signing & Capability, scroll down to the iCloud option. From the Containers option, you have to add a container for your data schema. Apple does not add the container automatically for us. BTW, if you add a container, you can’t delete the container. Apple has not given an option yet. You can also manage record types and any public data in the CloudKit console.

You can use your app bundle ID like this:

Select the container and then run the app. If you have multiple devices, you can log in and install the app on multiple deice. You will see, that if you add a new item, it automatically synchronises across all devices.

Add CloudKit to an existing project:

Let’s say we have a project and we did not enable SwiftData or CloudKit while creating the project. We can easily adopt Swift Data by importing SwiftData into our project. You can follow this article.

We can enable CloudKit from Signing & Capabilities. From the app’s Signing & Capabilities, click on + Capability. Search for iCloud and double-click to add to the project. Enable CloudKit in iCloud. Also, add a Container. You can use your app identifier like example.com.CloudKitDemo.

Click on + Capability again and add Background Modes. Then enable Remote Notifications.

That’s it. If you persist your data using SwiftData, everything will be synchronised automatically.

Simple project to demonstrate the process:

This is our data schema:

// Item.swift

import Foundation
import SwiftData

@Model
class Item {
var name: String!

init(name: String){
self.name = name
}
}

ContentView.swift:

// ContentView.swift
import SwiftUI
import SwiftData

struct ContentView: View {

@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var input: String = ""


var body: some View {
VStack {
HStack {
TextField("Add a task", text: $input )
Button("Add") {
let task = Item(name: input)
if !task.name.isEmpty{
modelContext.insert(task)
input = ""
}
}
}.padding()

List{
ForEach (items) { item in
Text(item.name ?? "")
}
}
}
.padding()
}
}

#Preview {
ContentView()
}

And YourAppNameApp.swift:

import SwiftUI
import SwiftData

@main
struct testSwiftDataApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Item.self)
}
}

Here you have to pass your model name. That’s it. Too easy right?

If you have multiple data models, try to follow Xcode generated template. Let’s say we have three models (Item.swift, Model2.swift, Model3.swift). We can generate a schema like this:

import SwiftUI
import SwiftData

@main
struct YourAppNameApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
Model2.self,
Model3.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()

var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}

BTW, as data is synchronized with iCloud, you will find it hard to delete data while developing your app. If you are looking for clean data you can try this:

            Button("Clean All Data"){
do {
try modelContext.delete(model: Item.self)

} catch {
print("Failed to clear data.")
}
}

If your app has multiple models, you can pass multiple models here. Like:

try modelContext.delete(model: Item.self)
try modelContext.delete(model: Model2.self)
try modelContext.delete(model: Model3.self)
..

You can’t delete a table or anything in iCloud. It will just clear the data from the table. That’s it.

Tips: As you can enable CloudKit anytime, I suggest you enable it before production or disable it while development & testing.

--

--