Post

Replies

Boosts

Views

Activity

Reply to Getting a list of deleted CloudKit records with an expired change token
Sorry for the long delay in responding! My implementation doesn't treat local data as just a local cache, or destroy any data on a fresh sync. CloudKit sync is totally optional in my app, so a user can turn it off or never use it, but would still want the data they see on their device preserved even if they do so. So, when I get an expired change token, my app does a "unify" operation where it tries to match all the local records and the cloud records together. If there are cloud records that don't exist locally, it creates them locally; if there are local records that don't exist in the cloud, it creates them in the cloud. The goal is to never, ever accidentally delete something and to give the user the ability to work without CloudKit. In essence, if there is no change token, it treats this like the first sync it's ever done with iCloud, where it needs to make sure that all data is merged into a single source of truth - rather than an incremental update where we are just making a few changes. However, there's an issue with this approach: if the user has two devices fully in sync, then deletes items on one device, and waits a few weeks before syncing their second device, the second device will: Get an expired change token. Begin the merge process. Create records in iCloud for all of the local records deleted on the first device (since they've been on the second device all along) Unfortunately, step 3 would include any items that the user deleted on their first device: they were on both devices when everything was in sync, and because the second device never got the chance to run an incremental sync and pull in those deletions, it has no idea that the items should be gone. I could easily resolve this problem if, on the second device, even with a change token of nil, CloudKit told me about every deletion that had happened since its last connection to the server. I could just go through that list and apply the deletions before running the merge algorithm. But if it only includes deletions made on the same device, my app doesn't have a way of knowing what the user has deleted since the last sync. Hopefully that's a little clearer than my first explanation. Basically, I want to know if the original answer or the second answer in this thread is correct: does it include everything since the last communication with CloudKit, or only things deleted on the same device? If the answer is the latter, I'm not really sure how to prevent deleted items from coming back like this.
Topic: App & System Services SubTopic: iCloud Tags:
2w
Reply to ExcUserFault and corrupted data when using UIImage#heicData
Thanks for the prompt and detailed response. Since UIImage(data: ...) does return nil for this kind of corrupted data, are there any downsides to checking for corrupt HEIC data with that rather than the Core Image API's? I checked through my codebase for any potential concurrency issues but all of the places where these calls happen are @MainActor. Unfortunately, we can't reliably reproduce it so it's tough to verify if a new version of iOS has fixed it or not. Thanks again for getting back to me so quickly and for taking a deep look into the problem.
Topic: UI Frameworks SubTopic: UIKit Tags:
May ’26
Reply to Getting a list of deleted CloudKit records with an expired change token
Hi Ziqiao, The important detail is step 3 - the user hasn't even opened Device B in weeks so that device is totally out of the loop. It's unaware of any deletions (or any changes at all) that have happened since step 1. Everything was synchronized initially, but then the user starts deleting items on Device A, which get synced up to iCloud. Meanwhile, back on device B, the app has been left unopened for weeks. When the user opens it up, they'll get .changeTokenExpired, and I'll fall back to the "unify" logic, the same behavior we use the first time this device syncs. I'm really confused about how deleted record ID's work. For my use case, it doesn't matter which device the record was created on - any device that has had a long delay between fetches such that it needs to do a full fetch will need a list of everything that was deleted on the server since the last fetch. For example, a user initially creates a record on Device A, syncs it to iCloud, Device B fetches. Later on, the user removes it on Device A and so my application code on Device A also removes the record from iCloud. Device B, weeks later, fetches for the first time. It needs to know that the record has been deleted since its last fetch, regardless of the fact that, sometime far in the distant past, Device A had originally created it. Does that make sense?
Topic: App & System Services SubTopic: iCloud Tags:
Feb ’26
Reply to Getting a list of deleted CloudKit records with an expired change token
Hi Ziqiao, thanks for the reply. Let me explain the situation I'm facing with my CloudKit-powered app. Device A and B are both fully synced with the cloud. On Device A, the user deletes a bunch of records and continues to use the app, letting the app sync in the background. The user waits a few weeks before opening the app on Device B. When they open the app, it attempts to sync. The change token is expired, so it retrieves all of the records on the server. My app is designed to treat this as if this is the user's first sync (a "unify" sync), so it compares all records on the server with all local records, then uploads any local records missing from the cloud and downloads any cloud records missing from the local database. At this point, all of the items deleted in step 2 have been re-downloaded onto Device B. Currently, my app never checks deletedRecordIDs on a unify sync. An easy fix would be to start processing this array even on unify syncs. But from your reply, it sounds like this will be an empty array on Device B because the deletions happened on Device A. Is that true? If so, how can I resolve this problem? Unfortunately, it's somewhat common for my users to have a second device that they use very infrequently, and they get annoyed when previously deleted records re-appear on their second device. To make my question more concrete, here's some CloudKit code demonstrating the scenario: // device A let newRecord = CKRecord(recordType: "Entry", recordID: CKRecord.ID(recordName: "test")) newRecord["name"] = "Length" try await self.database.save(newRecord) // device B let (_, _, savedChangeToken, _) = try await self.database.recordZoneChanges(inZoneWith: self.zoneID, since: nil) // successfully receives the Entry record with id "test" and saves locally // we also persist savedChangeToken somewhere on device B // device A try await self.database.deleteRecord(withID: CKRecord.ID(recordName: "test")) // several weeks pass // device B (uses savedChangeToken from above, which has now expired) let (_, deletions, _, _) = try await self.database.recordZoneChanges(inZoneWith: self.zoneID, since: savedChangeToken) // is the following true or false? deletions.map(\.recordID).contains(CKRecord.ID(recordName: "test")) If it's false, how do I resolve the problem described above (deleted records re-appearing on the rarely-used device)?
Topic: App & System Services SubTopic: iCloud Tags:
Jan ’26
Reply to Using a local model for Xcode Assist
Answering my own question: I was able to do this by downloading LM Studio and installing a Devstral model from Mistral. Just start the server for that model and then set up a local model in Xcode. It's a local model, and works even with wifi turned off. I found the model didn't work nearly as well at understanding even medium-sized codebases and making changes. It was better suited for writing small snippets of code or maybe analyzing a single file.
Dec ’25
Reply to Potential Structural Swift Concurrency Issue: unsafeForcedSync called from Swift Concurrent context
Thanks, Quinn. I think I normally wouldn't have worried too much about this, but since I'm in the midst of the Swift 6 concurrency migration, I was worried it was related to something in my code. I think the message should state more clearly that it's an internal speech synthesizer issue, not really a "structural Swift Concurrency issue". I filed FB20484368. Thanks again for your help!
Oct ’25
Reply to Troubles with CFBundleDocumentTypes and photos
Thanks for the response. I'm aware of share extensions, but they appear above the sharing app and are totally separate from the main app's data. Using a share extension is going to require breaking down my code into frameworks and also finding ways to update shared data that the share extension can use from my main app. If I can simply open the image in my app (as in the example apps I mentioned above, like Instagram, Obsidian, etc), then all of that complexity is gone. However, it seems like CFBundleDocumentTypes (at least as I've configured it) will only achieve what I want when sharing a single photo from the Photos app. It doesn't work in Safari or Mail, or if I've selected multiple photos in the Photos app. These other apps are able to do all of those things, and yet they switch control to the receiving app instead of just appearing over the sending app like a share extension. How can I do the same thing?
Topic: App & System Services SubTopic: General Tags:
Jan ’25
Reply to Unable to connect Xcode 15 to iPhone (Error: developer disk image could not be mounted)
None of these solutions helped me (though I didn't try erasing my phone). I know this post is about a year old, but if you're encountering this same issue today with Xcode 16 and the iPhone 16, the following post from an Apple engineer contains a solution: https://forums.developer.apple.com/forums/thread/764196?answerId=804884022#804884022
Oct ’24
Reply to Timing issue with updating two state variables
Still curious why this is happening. It also works fine if I use sheet(item: $url). But it doesn't work as above.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
Boosts
Views
Activity
2w
Reply to Getting a list of deleted CloudKit records with an expired change token
Sorry for the long delay in responding! My implementation doesn't treat local data as just a local cache, or destroy any data on a fresh sync. CloudKit sync is totally optional in my app, so a user can turn it off or never use it, but would still want the data they see on their device preserved even if they do so. So, when I get an expired change token, my app does a "unify" operation where it tries to match all the local records and the cloud records together. If there are cloud records that don't exist locally, it creates them locally; if there are local records that don't exist in the cloud, it creates them in the cloud. The goal is to never, ever accidentally delete something and to give the user the ability to work without CloudKit. In essence, if there is no change token, it treats this like the first sync it's ever done with iCloud, where it needs to make sure that all data is merged into a single source of truth - rather than an incremental update where we are just making a few changes. However, there's an issue with this approach: if the user has two devices fully in sync, then deletes items on one device, and waits a few weeks before syncing their second device, the second device will: Get an expired change token. Begin the merge process. Create records in iCloud for all of the local records deleted on the first device (since they've been on the second device all along) Unfortunately, step 3 would include any items that the user deleted on their first device: they were on both devices when everything was in sync, and because the second device never got the chance to run an incremental sync and pull in those deletions, it has no idea that the items should be gone. I could easily resolve this problem if, on the second device, even with a change token of nil, CloudKit told me about every deletion that had happened since its last connection to the server. I could just go through that list and apply the deletions before running the merge algorithm. But if it only includes deletions made on the same device, my app doesn't have a way of knowing what the user has deleted since the last sync. Hopefully that's a little clearer than my first explanation. Basically, I want to know if the original answer or the second answer in this thread is correct: does it include everything since the last communication with CloudKit, or only things deleted on the same device? If the answer is the latter, I'm not really sure how to prevent deleted items from coming back like this.
Topic: App & System Services SubTopic: iCloud Tags:
Replies
Boosts
Views
Activity
2w
Reply to Image playground and photorealistic images
For example, I don’t see an entry for a photographic style in the docs.
Replies
Boosts
Views
Activity
Jun ’26
Reply to ExcUserFault and corrupted data when using UIImage#heicData
Thanks for the prompt and detailed response. Since UIImage(data: ...) does return nil for this kind of corrupted data, are there any downsides to checking for corrupt HEIC data with that rather than the Core Image API's? I checked through my codebase for any potential concurrency issues but all of the places where these calls happen are @MainActor. Unfortunately, we can't reliably reproduce it so it's tough to verify if a new version of iOS has fixed it or not. Thanks again for getting back to me so quickly and for taking a deep look into the problem.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
May ’26
Reply to Getting a list of deleted CloudKit records with an expired change token
Hi Ziqiao, The important detail is step 3 - the user hasn't even opened Device B in weeks so that device is totally out of the loop. It's unaware of any deletions (or any changes at all) that have happened since step 1. Everything was synchronized initially, but then the user starts deleting items on Device A, which get synced up to iCloud. Meanwhile, back on device B, the app has been left unopened for weeks. When the user opens it up, they'll get .changeTokenExpired, and I'll fall back to the "unify" logic, the same behavior we use the first time this device syncs. I'm really confused about how deleted record ID's work. For my use case, it doesn't matter which device the record was created on - any device that has had a long delay between fetches such that it needs to do a full fetch will need a list of everything that was deleted on the server since the last fetch. For example, a user initially creates a record on Device A, syncs it to iCloud, Device B fetches. Later on, the user removes it on Device A and so my application code on Device A also removes the record from iCloud. Device B, weeks later, fetches for the first time. It needs to know that the record has been deleted since its last fetch, regardless of the fact that, sometime far in the distant past, Device A had originally created it. Does that make sense?
Topic: App & System Services SubTopic: iCloud Tags:
Replies
Boosts
Views
Activity
Feb ’26
Reply to Getting a list of deleted CloudKit records with an expired change token
Hi Ziqiao, thanks for the reply. Let me explain the situation I'm facing with my CloudKit-powered app. Device A and B are both fully synced with the cloud. On Device A, the user deletes a bunch of records and continues to use the app, letting the app sync in the background. The user waits a few weeks before opening the app on Device B. When they open the app, it attempts to sync. The change token is expired, so it retrieves all of the records on the server. My app is designed to treat this as if this is the user's first sync (a "unify" sync), so it compares all records on the server with all local records, then uploads any local records missing from the cloud and downloads any cloud records missing from the local database. At this point, all of the items deleted in step 2 have been re-downloaded onto Device B. Currently, my app never checks deletedRecordIDs on a unify sync. An easy fix would be to start processing this array even on unify syncs. But from your reply, it sounds like this will be an empty array on Device B because the deletions happened on Device A. Is that true? If so, how can I resolve this problem? Unfortunately, it's somewhat common for my users to have a second device that they use very infrequently, and they get annoyed when previously deleted records re-appear on their second device. To make my question more concrete, here's some CloudKit code demonstrating the scenario: // device A let newRecord = CKRecord(recordType: "Entry", recordID: CKRecord.ID(recordName: "test")) newRecord["name"] = "Length" try await self.database.save(newRecord) // device B let (_, _, savedChangeToken, _) = try await self.database.recordZoneChanges(inZoneWith: self.zoneID, since: nil) // successfully receives the Entry record with id "test" and saves locally // we also persist savedChangeToken somewhere on device B // device A try await self.database.deleteRecord(withID: CKRecord.ID(recordName: "test")) // several weeks pass // device B (uses savedChangeToken from above, which has now expired) let (_, deletions, _, _) = try await self.database.recordZoneChanges(inZoneWith: self.zoneID, since: savedChangeToken) // is the following true or false? deletions.map(\.recordID).contains(CKRecord.ID(recordName: "test")) If it's false, how do I resolve the problem described above (deleted records re-appearing on the rarely-used device)?
Topic: App & System Services SubTopic: iCloud Tags:
Replies
Boosts
Views
Activity
Jan ’26
Reply to Getting a list of deleted CloudKit records with an expired change token
Thanks, that's great to know! Just to understand it better: it will usually return all deleted records since the last change token, but if I pass nil, then which deleted records will I get: all deleted records ever? all deleted records since device last received data? something else? Just want to anticipate how many record ID's I should expect.
Topic: App & System Services SubTopic: iCloud Tags:
Replies
Boosts
Views
Activity
Jan ’26
Reply to Using a local model for Xcode Assist
Answering my own question: I was able to do this by downloading LM Studio and installing a Devstral model from Mistral. Just start the server for that model and then set up a local model in Xcode. It's a local model, and works even with wifi turned off. I found the model didn't work nearly as well at understanding even medium-sized codebases and making changes. It was better suited for writing small snippets of code or maybe analyzing a single file.
Replies
Boosts
Views
Activity
Dec ’25
Reply to Potential Structural Swift Concurrency Issue: unsafeForcedSync called from Swift Concurrent context
Thanks, Quinn. I think I normally wouldn't have worried too much about this, but since I'm in the midst of the Swift 6 concurrency migration, I was worried it was related to something in my code. I think the message should state more clearly that it's an internal speech synthesizer issue, not really a "structural Swift Concurrency issue". I filed FB20484368. Thanks again for your help!
Replies
Boosts
Views
Activity
Oct ’25
Reply to hidesBottomBarWhenPushed in iOS 26
This is resolved in beta 4 - thanks!
Topic: UI Frameworks SubTopic: UIKit
Replies
Boosts
Views
Activity
Jul ’25
Reply to hidesBottomBarWhenPushed in iOS 26
Seeing this too - I filed FB18022139
Topic: UI Frameworks SubTopic: UIKit
Replies
Boosts
Views
Activity
Jun ’25
Reply to CloudKit not storing or updating public data in real time.
Use CKContainer.publicCloudDatabase when running CloudKit operations. It sounds like you're using the private database. If this doesn't resolve your problem, include some relevant code or pseudocode in your post.
Topic: Design SubTopic: General Tags:
Replies
Boosts
Views
Activity
Apr ’25
Reply to Troubles with CFBundleDocumentTypes and photos
Thanks for the response. I'm aware of share extensions, but they appear above the sharing app and are totally separate from the main app's data. Using a share extension is going to require breaking down my code into frameworks and also finding ways to update shared data that the share extension can use from my main app. If I can simply open the image in my app (as in the example apps I mentioned above, like Instagram, Obsidian, etc), then all of that complexity is gone. However, it seems like CFBundleDocumentTypes (at least as I've configured it) will only achieve what I want when sharing a single photo from the Photos app. It doesn't work in Safari or Mail, or if I've selected multiple photos in the Photos app. These other apps are able to do all of those things, and yet they switch control to the receiving app instead of just appearing over the sending app like a share extension. How can I do the same thing?
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’25
Reply to CFBundleDocumentTypes and share extension.
This is an old question, but I'm having similar issues: changes to CFBundleDocumentTypes in my Info.plist just don't take effect, even after re-installing the app, cleaning and re-building, etc.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
Boosts
Views
Activity
Jan ’25
Reply to Unable to connect Xcode 15 to iPhone (Error: developer disk image could not be mounted)
None of these solutions helped me (though I didn't try erasing my phone). I know this post is about a year old, but if you're encountering this same issue today with Xcode 16 and the iPhone 16, the following post from an Apple engineer contains a solution: https://forums.developer.apple.com/forums/thread/764196?answerId=804884022#804884022
Replies
Boosts
Views
Activity
Oct ’24