My app does not knowingly capture or retain user data in any form, but I wanted to make sure that I don’t inadvertently have access to user data via CloudKit. I’m reaching out to confirm this.
Here’s the basic structure of the app - users can enter information which is then retained for their own use. I’m using SwiftData. @Model data is saved in a CloudKit container, which permits data syncing across iOS devices. I’ve never written any code to make the container database I’m using for the app (or zones within it) anything other than private, so I think (??) by default the data is stored privately. Data syncing does work and my icloud.developer.apple.com dashboard shows a private database subscription but no public or shared subscriptions.
My understanding of this design has been that the user’s data was stored in their personal, private iCloud account and I did NOT have access to it (nor do I need or want to have access).
However, then I watched this WWDC 2018 video on GDPR compliance (https://developer.apple.com/videos/play/tech-talks/703/), in which Michael Ford presents code that provides “visibility into the data that is saved in CloudKit for the user”, including the private database. This sounds to me like a developer might actually be able to access a user’s private database data.
Have I misinterpreted this WWDC 2018 presentation, has a developer’s access to user data in a private database changed since 2018, or do I actually have access to user data via my reliance on CloudKit to save user data?
Many thanks for your help!
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
I need to edit a persisted SwiftData item in an isolated context, because if not is isolated with every change triggers expensive computations.
When the edit is done and I save the temporal context. I expect that the temporal context is merged with the main context and all the views that have a dependency with the edited Item.id be invalidated and updated. But not. When I dismiss the ItemEditView and temporalContext.save() is triggered the ItemNormalView that has a dependency with the Item.id remains not updated so I have to do a non elegant either reliable workaround.
What is the canonical way of do it?
The most relevant code:
struct ItemEditView: View {
@Bindable var temporalItem: Item
let temporalContext: ModelContext
@Binding var updater: Bool
init(itemID: PersistentIdentifier,
container: ModelContainer,
updater: Binding<Bool>){
temporalContext = ModelContext(container)
temporalContext.autosaveEnabled = false
self.temporalItem = temporalContext.model(for: itemID) as! Item
self._updater = updater
}
var body: some View {
TextField("", text: $temporalItem.name)
.onDisappear{
Task{
try! temporalContext.save()
//Workaround:
try? await Task.sleep(nanoseconds:1_000_000_000)
updater.toggle()
}
}
}
}
struct ItemView: View {
let item: Item
@Environment(\.modelContext) private var modelContext
@State private var itemEditorViewModalViewIsPresented = false
@State private var updater = false
var body: some View {
VStack{
ItemNornalView(item: item, updater: $updater)
Button("Show Edit Modal View"){
itemEditorViewModalViewIsPresented.toggle()
}
}
.sheet(isPresented: $itemEditorViewModalViewIsPresented){
ItemEditView(itemID: item.id, container: modelContext.container, updater: $updater)
}
}
}
struct ItemNornalView: View {
let item: Item
@Binding var updater: Bool
var body: some View {
let _ = Self._printChanges()
Text("\(updater)").scaleEffect(0).hidden()
Text("Item name: \(item.name)")
}
}
The rest of the code:
struct ContentView: View {
var body: some View {
TabView{
MetricsView().tabItem { Text("Metrics") }
ItemsList().tabItem { Text("List") }
}
}
}
struct MetricsView: View {
@Query private var items: [Item]
var body: some View {
Chart{
BarMark(x: .value("x", "Average"), y: .value("y", expensiveComputation(items: items)))
}
}
private func expensiveComputation(items: [Item])->Double{
//...
return 1.0
}
}
struct ItemsList: View {
@Query private var items: [Item]
@Environment(\.modelContext) private var modelContext
@State var path = NavigationPath()
var body: some View {
let _ = Self._printChanges()
NavigationStack(path: $path) {
List {
ForEach(items) { item in
Button{
path.append(item)
}label:{
ItemRowView(item: item)
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
.navigationDestination(for: Item.self){ item in
ItemView(item:item)
}
}
}
private func addItem() {
withAnimation {
let newItem = Item()
modelContext.insert(newItem)
try! modelContext.save()
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
Hi Forum,
I am wondering how I can filter the data retrieving from CloudKit with SwiftData and using the #Predicate macro?
I always get this error message:
Thread 1: Fatal error: Predicate does not support keypaths with multiple components
Does someone know how to add two predicates and use CloudKit?
Thanks in advance 😊
I am developing a SwiftData application using CloudKit.
I have been playing with the app for a while and populated the database.
Recently I decided to change the type of an attribute. So I reseted the scheme in CloudKit Console to the production one, which actually is empty for now, so I restart from scratch.
Unfortunatly, the locally cached database on my Mac is using the old schema and my app fails loading the ModelContainer because of the type incompatibilities.
I would like to delete the locally cached database but I haven't a clue of where it can be. I do not want to go through a migration plan for that.
Does someone know where can I find it ?
Chris tof
I have set a core data structure based on two entities :
first entity is "Mot" (Word) with one String attribute "graphie" (spelling) and one ToOne relationship "definition" with the second entity.
second entity is "Definition" that has one String attribute "enonce" (statement) and on ToMany relationship "mot" with entity Mot.
One word can have several spellings but only one definition.
I managed to load several rows of data and now, when I select one word I want to display all the spellings, which means all "Mot" that have the same definition.
In my content view I have a List based on a request:
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Mot.entity(), sortDescriptors: [NSSortDescriptor(key: "graphie", ascending: true)])
private var mots: FetchedResults<Mot>
var body: some View {
NavigationView {
...
List {
ForEach(mots) { (mot: Mot) in
HStack {
Text(mot.graphie ?? "Non trouvé")
Spacer()
Text(mot.definition!.enonce ?? "Non trouvé")
}
.onTapGesture {
motAffiche = mot
tfMot = mot.graphie ?? "Not found"
tfDefinition = mot.definition?.enonce ?? "Not found"
tfOrthoAlt = returnOrthoAlt(mot: mot)
}
}
}
the function returnOrthoAlt(mot) is supposed to return a string with all spellings separated by commas.
What is working for the moment, but not satisfactory is the following code for that function
private func returnOrthoAlt(mot: Mot) -> String {
var ort = [String]()
for elem in mots {
if elem.definition!.enonce! == mot.definition!.enonce! && elem != mot {
ort.append(elem.graphie!)
let defObjId = elem.definition!.objectID.uriRepresentation()
print("\(elem.graphie!) def: \(elem.definition!.enonce!) \(defObjId) ")
}
}
return if !ort.isEmpty { ort.joined(separator: ", ")} else {""}
}:
I am going through the table 'mots' of FetchedResults to find those that have the same definition as the current 'mot'. What I find not satisfactory is that I am comparing the 'mot.definition.enonce' of each Mot, which is not supposed to be unique, instead of 'mot.definition' directly, which did not work. The objects 'mot.definition' are obviously not equal (neither with == nor with === operators).
Also I tried to retrieve the reverse relation directly with 'mot.definition.mot' but that returned a NSSet which, converted to a set seems to contain only one object, that is 'mot' itself.
One possibility of course, would be to ad a unique Id to the entity 'Definition' but I seem to understand that this is not the recommended practice, the more so as swift does not provide system generated id.
What do I miss in the core data concept ? Can someone help me out ?
In WWDC2020, the video "Synchronizing a local store to the cloud" shows some data fields in the left column of the dashboard, such as CD_post, CD_Tag, where did these come from? need to create it manually on dababoard, or it synchronize from the demo app ?
why do I run this corresponding demo, but there are no data fields in the dashboard of my account
and my dashboard of my account is almost empty?
Hello,
I want to build an app that will allow the user to entry some health related records and be synced with the HealthKit. Any record that is in the HealthKit is stored locally on the device.
I find it conceptually unsure if I should be storing the HealthKit records in the SwiftData to make the user records available across the iCloud synced devices.
Should I read the HealthKit record, make a copy of it (including it's ID) on my app's data in SwiftData?
How the syncing should be done the right way?
thanks.
I am writing code to be able to perform Undo&Redo with SwiftUI and SwiftData. Unlike usual, there is a Model of SwiftData in the Observable class,
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true, isUndoEnabled: true)
@Environment(\.undoManager) var undoManager
but it doesn't work. How should I describe the isUndoEnabled: true option and the undoManager?
import SwiftData
@Observable class SwiftDataCoordinator {
static let shared = SwiftDataCoordinator()
var items = [Item]()
let modelContainer: ModelContainer = {
let schema = Schema([
Item.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
}
Dear members,
The lack of enumeration during the documentation has lead to the situation where the sizing of the text can no longer be used to categorise anything. Take an example of the Apple Technologies (where the standardised way to read and type both are missing).
Also, suggestions from the members related to running the numbers (about updating the real-time prices of the underlying assets) in the iOS application and the usage of the most relevant frameworks will be highly appreciated.
Waiting to hear back soon.
I created an app using SwiftData to store titles, dates, and images. When creating and deleting data models repeatedly, the app's storage space should remains the same, but for me it steadily accumulate so now its 5GB in the app's settings despite having no data. Why is this happening?
Here is my basic problem. The app itself builds without issue, but when I simulate I get the following log in the debug console. The app also force closes when the Save function is selected with these errors. The app is a simple form which saves the data to a CoreData entity. It will do more (full CRUD) later after I deal with this particular issue. I have checked and rechecked class names, spelling issues, case sensitivities. I can not seem to find the issue.
error: No NSEntityDescription in any model claim the NSManagedObject subclass 'App.Entity' so +entity is confused. Have you loaded your NSManagedObject Model yet?
Also:
error: +[App.Entity entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
Hello everyone,
I followed this (https://www.hackingwithswift.com/quick-start/swiftdata/how-to-sync-swiftdata-with-icloud) guide from Paul Hudson on how to sync swiftdata with iCloud. I tried it on my device directly and it worked exactly as I would expect. Now I tried the same version of the app on another device installed through TestFlight external tester group. It no longer works.
When deleting the app, the alert reads "Deleting this app will also delete its data, but any documents or data will be stored in iCloud will not be deleted" so the app should have said something in iCloud. When looking in Settings -> Your Name -> iCloud -> Manage Account Storage, on the working device I can see around 300 KB saved in iCloud, on the other device my app is not listed. Both have a fast and working internet connection, almost fully charged, low data mode turned off, running 17.4.1, Background modes enabled, Mobile data enabled, more than enough unused iCloud storage and plenty of time to sync.
I was streaming the logs from the Cloudkit dashboard but nothing appeared there. The data saved neither syncs to another device of the same apple id nor reappears after app removal.
The model Container is initialized without any configuration, the only relationship is optional and every value has a default value. There is A LOT of log noise when launching the app and I am unable to get any meaningful information from that. I can only get the log from the device where it is working as expected.
I have triple checked that it is exactly the same app version and no debug conditions anywhere. I have absolutely no idea what is causing this.
Thanks for any help :)
I have a model class with various status values that I would like to store in a single 64 bits integer. For example one bit for this boolean flag, 4 bits for that lookup value and so on.
I tried to create a predicate containing bitwise operators to query specific bits but that seems impossible, no way no compile it.
is there a way I am no aware of to accomplish this, some workaround or is that planned to be possible in future Swiftdata release?
that would be a shame having to store a few status values of 4 or less bits each in different 64bits values, what a waste!
Thanks
Tof
My app is currently on the app store for IOS, iPadOS and MacOS. I have created a version for Apple TV but cannot get it to save to iCloud. Is there anyone who can help me get my app to save to iCloud for the Apple TV?
Hello I have a TestFlight tester getting a random repeated crash when the app is interacting with core data. Every time it crashes we get the call stack like below from line 6 stating -NSManagedObject _processRecentChanges.
Searching for _sharedIMPL_setvfk_core it seems to indicate a multithreading issue but my managedObjectContext is initialized with main queue concurrency.
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil)
{
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
When I pulled the cashpoint in to the Xcode project everything appears to be running on the main thread.
I can't seem to find documentation on what might be causing the issue. Any pointers would be appreciated. The app is compiled on Xcode 15 targeting iOS 15. Users device is iOS 17.3.1.
Hey,
I'm currently working on adding CloudKit support to the GRDB SQLite database in my app. CKSyncEngine, though still a bit tricky to wrap your head around, is amazing!
I have most of the basic setup implemented and some very easy cases already work, which is really exciting!
However, I do have some questions regarding data consistency across devices. I'm not sure though, that these questions are actually "correct" to ask. Maybe my entire approach is inherently flawed.
Say we add two records to the pending changes of the sync engine:
// I'm simplifying CKRecord.ID to be a String here
syncEngine.state.add(pendingRecordZoneChanges: [.saveRecord("1"), .saveRecord("2")]
Let's also say that both records are tightly connected. For example, they could be in a one-to-one relationship and must always be added to the database together because the app relies on the existence of either none or both.
After that call, at some later point, the system will call the sync engine's delegate nextRecordZoneChangeBatch(_:syncEngine:) for us to batch the changes together to send to iCloud.
First question: Can we guarantee that records "1" and "2" always land in the exact same batch, and are never separated?
Looking at the example code, there are two line that worry me a bit:
// (Sample project: `SyncedDatabase.swift, lines 132-133`)
let scope = context.options.scope
let changes = syncEngine.state.pendingRecordZoneChanges.filter { scope.contains($0) }
The scope could lead to one of the two records being filtered out. However, my assumption is that the scope will always be .all when the system calls it from an automatically managed schedule, and only differs when you manually specify a different value through calling syncEngine.sendChanges(_:). Is that correct?
Now back to the example. Say we successfully batched records "1" and "2" together and the changes have been sent to iCloud. Awesome!
What happens next? Other connected devices will, at some point, fetch those changes and apply them to their respective local databases.
Second question: Can we guarantee that iCloud sends the exact same batches from earlier to other devices and does not create new batches from all the changes?
I'm worried that iCloud might take all stored changes and "re-batch" them for whatever reason (efficiency, for example). Because that could cause records "1" and "2" to be separated into different batches. A second device could end up receiving "1" and, for at least some period of time, will not receive "2", which would be an "illegal" state of the data.
I'd appreciate any help or clarification on this :)
Thanks a lot!
Since a couple days ago, CloudKit database queries in my app and on the CloudKit dashboard fail with the error "request failed with http status code 500". Nothing significant changed in the app recently, and it has otherwise worked well most of the time for more than a year, although CloudKit seems to have outages somewhat frequently. My app is mostly unusable without access to the CloudKit database, so this is a critical issue.
I have also filed a Feedback report (FB13709321) on this issue.
Hi,
I started a new project selecting SwiftData as db by default.
I created my models as default.
@Model
class Trip {
var id: UUID
public private(set) var Name: String
public private(set) var Members: [Member]
public private(set) var Buys: [Buy]
public func removeBuy(indexBuyToRemove: Int) {
self.Buys.remove(at: indexBuyToRemove)
}
}
@Model
class Member: Identifiable, Equatable {
var id: UUID
public private(set) var Name: String
}
@Model
class Buy: Identifiable, Equatable {
var id: UUID
public private(set) var Author: Member
public private(set) var Ammount: Double
public private(set) var Title: String
}
I initialize my trips as this in my ContentView
@Query public var tripsList: [Trip]
And then pass this list in every other view.
I also have a ModelContext as default, passed like this in the @Main
@main
struct MainApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Member.self, Buy.self, Trip.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()
.preferredColorScheme(.dark)
}
.modelContainer(sharedModelContainer)
}
}
And defining in the other views like this
@Environment(\.modelContext) var modelContext
Now. For the problem of consistency.
Whenever I add a new Trip I do this or delete (as .onDelete in the List)
public func addTrip(Trip: Trip) {
modelContext.insert(Trip)
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(tripsList[index])
}
}
}
And everything works fine.
The problem starts when I try to delete a Buy element of the Trip.Buys: [Buy].
I do like this:
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
let buyToRemove = trip.Buys[index]
trip.removeBuy(indexBuyToRemove: index)
do {
try modelContext.save()
} catch {}
}
}
}
The delete is not consistent.
What am I missing? What am I doing wrong?
Thank you for your help
Is there a way to view the data saved when using swiftdata? Even after deleting all models, the storage space taken up by the app in Settings is too large.
Porting some Core Data code that was written several years ago. It has an entity called Transaction.
This pre-dates Transaction that appeared in Animation.
So Apple is now colliding with my naming.
Looks like light weight migration isn't going to do the trick and I need to do more work for migration to work.
Checking whether there is some magical use of namespace where I could separate my entity use of Transaction. Or it's full weight migration...