We have an unreleased SwiftData app for iOS18+. While we were testing I saw reports on the forum about unexpected database migrations for codable arrays on iOS26.1.
I'd like to ask a couple of questions:
1- Does this issue originate from the new Xcode version, or is it specific to iOS 26.1?
2- Is it possible to change our attribute so that users on older iOS versions receive the same model, preventing a migration from being triggered when they upgrade to iOS 26.1?
One of our models looks like this:
struct Point: Codable, Hashable {
let x: Int
let y: Int
}
@Model
class Grid {
private(set) var gridId: String = ""
var points: [Point] = []
var updatedAt: Date = Date()
private(set) var createdAt: Date = Date()
#Index<Grid>([\.gridId])
...
}
I can think of some options like:
// 1
@Attribute(.transformable(by: CustomJsonTransformer.self)) var points: [Point] = []
// 2
@Attribute(.externalStorage) var points: [Point] = []
// 3
var points: Data = Data() // store points as data
However, I'm not sure which one to use.
What would you recommend to handle this, or is there a better strategy you would suggest?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
What have people's experience with converting locally stored app data to a more browser based accessible format? Firebase seems expensive, Subabase a bit more challenging, and CloudKit too restrictive.
Hello, thank you Apple for supporting custom store with SwiftData and the Schema type is superb to work with. I have successfully set one up with SQL and have some feedback and issues regarding its APIs.
There’s a highlighted message in the documentation about not using internal restricted symbols directly, but they contradict with the given protocols and I am concerned about breaking any App Store rules. Are we allowed to use these? If not, they should be opened up as they’re useful.
BackingData is required to set up custom snapshots, initialization, and getting/setting values. And I want to use it with createBackingData() to directly initialize instances from snapshots when transferring them between server and client or concurrency.
RelationshipCollection for casting to-many relationships from backing data or checking if an array contains a PersistentModel.
SchemaProperty for type erasure in a collection.
Schema.Relationship has KeyPath properties, but it is missing for Schema.Attribute and Schema.CompositeAttribute. Which means you can’t purely depend on the schema to map data. I am unable to access the properties of a custom struct type in a predicate unless I use Mirror with schemaMetadata() or CustomStringConvertible on the KeyPath directly to extract it.
Trivial, but… the KeyPath property name is inconsistent (it’s all lowercase).
It would be nice to retrieve property names from custom struct types, since you are unable access CodingKeys that are auto synthesized by Codable for structs. But I recently realized they’re a part Schema.CompositeAttribute, however I don’t know how to match these without the KeyPath…
I currently map my entities using CodingKeys to their PredicateCodableKeyPathProviding.… but I wish for a simpler alternative!
It’s unclear how to provide the schema to the snapshot before new models are created.
I currently use a static property, but I want to make it flexible if more schemas and configurations are added later on.
I considered saving and loading the schema in a temporary location, but doubtful that the KeyPath values will be available as they are not Codable.
I suspect schemaMetadata() has the information I need to map the backing data without a schema for snapshots, but as mentioned previously, properties are inaccessible…
Allow access to entity metatypes, like value types from SchemaProperty. They’re useful for getting data out of snapshots and casting them to CodingKeys and PredicateCodableKeyPathProviding. They do not carry over when you provide them in the Schema.
I am unable to retrieve the primary key from PersistentIdentifier.
It seems like once you create one, you can’t get it out, like the DataStoreConfiguration in ModelContainer is not the one you used to set it up. I cannot cast it, it is an entirely different struct?
I have to use JSONSerialization to extract it, but I want to get it directly since it is not a column in my database. It is transformed when it goes to/from my tables.
It’s unknown how to support some schema options, such as Spotlight and CloudKit.
Allow for extending macro options, such as adding options to set as primary key, whether to auto increment, etc…
You can create a schema for super and sub entities, but it doesn’t appear you can actually set them up from the @Model macro or use inheritance on these models…
SwiftData history tracking seems incomplete for HistoryDelete, because that protocol requires HistoryTombstone, but this type cannot be instantiated, nor does it contain anything useful to infer from.
As an aside, I want to create my own custom ModelActor that is a global actor. However, I’m unable to replicate the executor that Apple provides where the executor has a ModelContext, because this type does not conform to Sendable. So how did Apple do this? The documentation doesn’t mention unchecked Sendable, but I figure if the protocol is available then we would be able to set up our own.
And please add concurrency features!
Anyway, I hope for more continued support in the future and I am looking forward to what’s new this WWDC! 😊
We are using AgeRangeService.requestAgeRange(ageGates:in:) with an age gate of 18 to verify adult users.
The system prompt always displays the lower-bound wording (“17 or Younger”), even when the app’s requirement is to verify users who are 18 or older. We understand the UI is system-controlled; however, this wording causes confusion for users, QA, and product teams, as it appears to indicate a child-only flow even when requesting adult verification.
Based on the demonstration video, it appears that they have another more coherent message.
In Apple's example, it is different, and it is correct that we need to specify 18 years or older in the implementation.
A little more context might be helpful, but we are creating a kind of wrapper with React Native that receives that value as a parameter, which is 18.
Hi everyone,
Im trying to set up CloudKit for my Unreal Engine 5.4 project but seem to be hitting some roadblocks on how to set up the Record Types.
From my understanding I need to set up a "file" record type with a "contents" asset field - but even with this it doesn't seem to work :(
Any unreal engine devs with some experience on this who could help me out?
Thanks!
Hi all,
I am using SwiftData and cloudkit and I am having an extremely persistent bug.
I am building an education section on a app that's populated with lessons via a local JSON file. I don't need this lesson data to sync to cloudkit as the lessons are static, just need them imported into swiftdata so I've tried to use the modelcontainer like this:
static func createSharedModelContainer() -> ModelContainer {
// --- Define Model Groups ---
let localOnlyModels: [any PersistentModel.Type] = [
Lesson.self, MiniLesson.self,
Quiz.self, Question.self
]
let cloudKitSyncModels: [any PersistentModel.Type] = [
User.self, DailyTip.self, UserSubscription.self,
UserEducationProgress.self // User progress syncs
]
However, what happens is that I still get Lesson and MiniLesson record types on cloudkit and for some reason as well, whenever I update the data models or delete and reinstall the app on simulator, the lessons duplicate (what seems to happen is that a set of lessons comes from the JSON file as it should), and then 1-2 seconds later, an older set of lessons gets synced from cloudkit.
I can delete the old set of lessons if I just delete the lessons and mini lessons record types, but if I update the data model again, this error reccurrs.
Sorry, I don't know if I managed to explain this well but essentially I just want to stop the lessons and minilessons from being uploaded to cloudkit as I think this will fix the problem. Am I doing something wrong with the code?
I have a SwiftData model where I need to customize behavior based on the value of a property (connectorType). Here’s a simplified version of my model:
@Model
public final class ConnectorModel {
public var connectorType: String
...
func doSomethingDifferentForEveryConnectorType() {
...
}
}
I’d like to implement doSomethingDifferentForEveryConnectorType in a way that allows the behavior to vary depending on connectorType, and I want to follow best practices for scalability and maintainability. I’ve come up with three potential solutions, each with pros and cons, and I’d love to hear your thoughts on which one makes the most sense or if there’s a better approach:
**Option 1: Use switch Statements
**
func doSomethingDifferentForEveryConnectorType() {
switch connectorType {
case "HTTP":
// HTTP-specific logic
case "WebSocket":
// WebSocket-specific logic
default:
// Fallback logic
}
}
Pros: Simple to implement and keeps the SwiftData model observable by SwiftUI without any additional wrapping.
Cons: If more behaviors or methods are added, the code could become messy and harder to maintain.
**Option 2: Use a Wrapper with Inheritance around swiftdata model
**
@Observable
class ParentConnector {
var connectorModel: ConnectorModel
init(connectorModel: ConnectorModel) {
self.connectorModel = connectorModel
}
func doSomethingDifferentForEveryConnectorType() {
fatalError("Not implemented")
}
}
@Observable
class HTTPConnector: ParentConnector {
override func doSomethingDifferentForEveryConnectorType() {
// HTTP-specific logic
}
}
Pros: Logic for each connector type is cleanly organized in subclasses, making it easy to extend and maintain.
Cons: Requires introducing additional observable classes, which could add unnecessary complexity.
**Option 3: Use a @Transient class that customizes behavior
**
protocol ConnectorProtocol {
func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel)
}
class HTTPConnectorImplementation: ConnectorProtocol {
func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) {
// HTTP-specific logic
}
}
Then add this to the model:
@Model
public final class ConnectorModel {
public var connectorType: String
@Transient
public var connectorImplementation: ConnectorProtocol?
// Or alternatively from swiftui I could call myModel.connectorImplementation.doSomethingDifferentForEveryConnectorType() to avoid this wrapper
func doSomethingDifferentForEveryConnectorType() {
connectorImplementation?.doSomethingDifferentForEveryConnectorType(connectorModel: self)
}
}
Pros: Decouples model logic from connector-specific behavior. Avoids creating additional observable classes and allows for easy extension.
Cons: Requires explicitly passing the model to the protocol implementation, and setup for determining the correct implementation needs to be handled elsewhere.
My Questions
Which approach aligns best with SwiftData and SwiftUI best practices, especially for scalable and maintainable apps?
Are there better alternatives that I haven’t considered?
If Option 3 (protocol with dependency injection) is preferred, what’s the best way to a)manage the transient property 2) set the correct implementation and 3) pass reference to swiftdata model?
Thanks in advance for your advice!
Hi,
I'm trying to sign in with Apple CloudKit.
I'm using the following code:
'use client';
import { CLOUDKIT_CONSTANTS } from '@/constants/cloudkit';
import { setCloudKitConfigured } from '@/lib/cloudkitSingleton';
import { CloudKitStatic } from '@/types/cloudkit';
import Script from 'next/script';
declare global {
interface Window {
CloudKit: CloudKitStatic;
}
}
export default function Home() {
const initializeCloudKit = async () => {
console.info('⭐️ initializeCloudKit - start');
// 古い認証情報を削除
try {
// LocalStorageから古い認証情報を削除
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.includes('cloudkit') || key.includes('CloudKit'))) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => localStorage.removeItem(key));
// SessionStorageからも削除
const sessionKeysToRemove = [];
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
if (key && (key.includes('cloudkit') || key.includes('CloudKit'))) {
sessionKeysToRemove.push(key);
}
}
sessionKeysToRemove.forEach(key => sessionStorage.removeItem(key));
console.log('古い認証情報を削除しました');
} catch (cleanupError) {
console.warn('認証情報のクリーンアップ中にエラー:', cleanupError);
}
try {
const cloudKit = window.CloudKit.configure({
containers: [
{
containerIdentifier: 'XXXXXX',
apiTokenAuth: {
apiToken: 'XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX',
persist: false,
signInButton: {
id: 'cloudkit-sign-in-button',
theme: 'black',
},
signOutButton: {
id: 'cloudkit-sign-out-button',
theme: 'black',
},
},
environment: 'development',
},
],
});
console.info('⭐️ cloudKit', cloudKit);
setCloudKitConfigured(true);
const container = cloudKit.getDefaultContainer();
console.info('⭐️ CloudKit configured, setting up auth...');
// 初期認証状態をチェック
try {
const initialUser = await container.setUpAuth();
console.info('⭐️ setUpAuth result:', initialUser);
} catch (authError) {
console.info('⭐️ setUpAuth error (expected for unauthenticated):', authError);
}
// CloudKitの標準コールバックも併用(念のため)
try {
container.whenUserSignsIn().then((userInfo: any) => {
console.info('⭐️ CALLBACK: whenUserSignsIn fired!', userInfo);
});
container.whenUserSignsOut().then(() => {
console.info('⭐️ CALLBACK: whenUserSignsOut fired!');
});
} catch (callbackError) {
console.info('⭐️ Callback setup error (non-critical):', callbackError);
}
console.info('⭐️ initializeCloudKit - completed');
} catch (error) {
console.error('⭐️ Critical CloudKit initialization error:', error);
}
};
return (
<>
<Script
src="https://cdn.apple-cloudkit.com/ck/2/cloudkit.js"
strategy="afterInteractive"
onLoad={() => {
initializeCloudKit();
}}
onError={error => {
console.error('⭐️ CloudKit initialization error:', error);
}}
/>
<div id="cloudkit-sign-in-button" />
<div id="cloudkit-sign-out-button" />
</>
);
}
In Chrome secret tab, I can sign in successfully.
But in Chrome normal tab, I can't sign in.
In normal tab, following error occurs on sign in button click:
cloudkit.js:14 Uncaught (in promise) Error: UNKNOWN_ERROR
cloudkit.js:14 GET https://api.apple-cloudkit.com/database/1/XXXXXX/XXXXXX/public/users/caller?ckjsBuildVersion=2420ProjectDev22&ckjsVersion=2.6.4&clientId=XXXXX-XXXXXXX-XXXX-XXXXX&
ckAPIToken=XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX
421 (Misdirected Request)
I think, cloudkit instance has re-initialized when I click the sign in button only in normal tab.
So I can't sign in.
Do you have any idea what might be causing the error ?
Thanks in advance for your help!
Hello everyone,
I am experiencing a persistent authentication error when querying a custom user profile record, and the error message seems to be a red herring.
My Setup:
I have a custom CKRecord type called ColaboradorProfile.
When a new user signs up, I create this record and store their hashed password, salt, nickname, and a custom field called loginIdentifier (which is just their lowercase username).
In the CloudKit Dashboard, I have manually added an index for loginIdentifier and set it to Queryable and Searchable. I have deployed this schema to Production.
The Problem:
During login, I run an async function to find the user's profile using this indexed loginIdentifier.
Here is the relevant authentication code:
func autenticar() async {
// ... setup code (isLoading, etc.)
let lowercasedUsername = username.lowercased()
// My predicate ONLY filters on 'loginIdentifier'
let predicate = NSPredicate(format: "loginIdentifier == %@", lowercasedUsername)
let query = CKQuery(recordType: "ColaboradorProfile", predicate: predicate)
// I only need these specific keys
let desiredKeys = ["password", "passwordSalt", "nickname", "isAdmin", "isSubAdmin", "username"]
let database = CKContainer.default().publicCloudDatabase
do {
// This is the line that throws the error
let result = try await database.records(matching: query, desiredKeys: desiredKeys, resultsLimit: 1)
// ... (rest of the password verification logic)
} catch {
// The error always lands here
logDebug("Error authenticating with CloudKit: \(error.localizedDescription)")
await MainActor.run {
self.errorMessage = "Connection Error: \(error.localizedDescription)"
self.isLoading = false
self.showAlert = true
}
}
}
The Error:
Even though my query predicate only references loginIdentifier, the catch block consistently reports this error:
Error authenticating with CloudKit: Field 'createdBy' is not marked queryable.
I know createdBy (the system creatorUserRecordID) is not queryable by default, but my query isn't touching that field. I already tried indexing createdBy just in case, but the error persists. It seems CloudKit cannot find or use my index for loginIdentifier and is incorrectly reporting a fallback error related to a system field.
Has anyone seen this behavior? Why would CloudKit report an error about createdBy when the query is explicitly on an indexed, custom field?
I'm new to Swift and I'm struggling quite a bit.
Thank you,
I am using SwiftData with CloudKit to synchronize data across multiple devices, and I have encountered an issue: occasionally, abnormal sync behavior occurs between two devices (it does not happen 100% of the time—only some users have reported this problem). It seems as if synchronization between the two devices completely stops; no matter what operations are performed on one end, the other end shows no response.
After investigating, I suspect the issue might be caused by both devices simultaneously modifying the same field, which could lead to CloudKit's logic being unable to handle such conflicts and causing the sync to stall. Are there any methods to avoid or resolve this situation?
Of course, I’m not entirely sure if this is the root cause. Has anyone encountered a similar issue?
Hi !
Would anyone know (if possible) how to create backup files to export and then import from the data recorded by SwiftData?
For those who wish, here is a more detailed explanation of my case:
I am developing a small management software with customers and events represented by distinct classes. I would like to have an "Export" button to create a file with all the instances of these 2 classes and another "Import" button to replace all the old data with the new ones from a previously exported file.
I looked for several solutions but I'm a little lost...
SwiftData crashes 100% when fetching history of a model that contains an optional codable property that's updated:
SwiftData/Schema.swift:389: Fatal error: Failed to materialize a keypath for someCodableID.someID from CrashModel. It is possible that this path traverses a type that does not work with append(), please file a bug report with a test.
Would really appreciate some help or even a workaround.
Code:
import Foundation
import SwiftData
import Testing
struct VaultsSwiftDataKnownIssuesTests {
@Test
func testCodableCrashInHistoryFetch() async throws {
let container = try ModelContainer(
for: CrashModel.self,
configurations: .init(
isStoredInMemoryOnly: true
)
)
let context = ModelContext(container)
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
// 1: insert a new value and save
let model = CrashModel()
model.someCodableID = SomeCodableID(someID: "testid1")
context.insert(model)
try context.save()
// 2: check history it's fine.
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
// 3: update the inserted value before then save
model.someCodableID = SomeCodableID(someID: "testid2")
try context.save()
// The next check will always crash on fetchHistory with this error:
/*
SwiftData/Schema.swift:389: Fatal error: Failed to materialize a keypath for someCodableID.someID from CrashModel. It is possible that this path traverses a type that does not work with append(), please file a bug report with a test.
*/
try SimpleHistoryChecker.hasLocalHistoryChanges(context: context)
}
}
@Model final class CrashModel {
// optional codable crashes.
var someCodableID: SomeCodableID?
// these actually work:
//var someCodableID: SomeCodableID
//var someCodableID: [SomeCodableID]
init() {}
}
public struct SomeCodableID: Codable {
public let someID: String
}
final class SimpleHistoryChecker {
static func hasLocalHistoryChanges(context: ModelContext) throws {
let descriptor = HistoryDescriptor<DefaultHistoryTransaction>()
let history = try context.fetchHistory(descriptor)
guard let last = history.last else {
return
}
print(last)
}
}
Testing Environment: iOS 18.4.1 / macOS 15.4.1
I am working on an iOS project that aims to utilize the user's iCloud Drive documents directory to save a specific directory-based file structure. Essentially, the app would create a root directory where the user chooses in iCloud Drive, then it would populate user generated files in various levels of nested directories.
I have been attempting to use NSMetadataQuery with various predicates and search scopes but haven't been able to get it to directly monitor changes to files or directories that are not in the root directory.
Instead, it only monitors files or directories in the root directory, and any changes in a subdirectory are considered an update to the direct children of the root directory.
Example
iCloud Drive Documents (Not app's ubiquity container)
User Created Root Directory (Being monitored)
File A
Directory A
File B
An insertion or deletion within Directory A would only return a notification with userInfo containing data for NSMetadataQueryUpdateChangedItemsKey relating to Directory A, and not the file or directory itself that was inserted or deleted. (Query results array also only contain the direct children.)
I have tried all combinations of these search scopes and predicates with no luck:
query.searchScopes = [
rootDirectoryURL,
NSMetadataQueryUbiquitousDocumentsScope,
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope,
]
NSPredicate(value: true)
NSPredicate(format: "%K LIKE '*.md'", NSMetadataItemFSNameKey)
NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, url.path(percentEncoded: false))
I do see these warnings in the console upon starting my query:
[CRIT] UNREACHABLE: failed to get container URL for com.apple.CloudDocs
[ERROR] couldn't fetch remote operation IDs: NSError: Cocoa 257 "The file couldn’t be opened because you don’t have permission to view it."
"Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""
But I am not sure what to make of that, since it does act normally for finding updates in the root directory.
Hopefully this isn't a limitation of the API, as the only alternative I could think of would be to have multiple queries running for each nested directory that I needed updates for.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Files and Storage
iCloud Drive
Foundation
I have developed an podcast app, where subscriped podcast & episodes synched with iCloud.
So its working fine with iOS & iPad with latest os version, but iCloud not synching in iPod with version 15.
Please help me to fix this.
Thanks
Devendra K.
I'm trying to build a custom FetchRequest that I can use outside a View. I've built the following ObservableFetchRequest class based on this article: https://augmentedcode.io/2023/04/03/nsfetchedresultscontroller-wrapper-for-swiftui-view-models
@Observable @MainActor class ObservableFetchRequest<Result: Storable>: NSObject, @preconcurrency NSFetchedResultsControllerDelegate {
private let controller: NSFetchedResultsController<Result.E>
private var results: [Result] = []
init(context: NSManagedObjectContext = .default, predicate: NSPredicate? = Result.E.defaultPredicate(), sortDescriptors: [NSSortDescriptor] = Result.E.sortDescripors) {
guard let request = Result.E.fetchRequest() as? NSFetchRequest<Result.E> else {
fatalError("Failed to create fetch request for \(Result.self)")
}
request.predicate = predicate
request.sortDescriptors = sortDescriptors
controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
super.init()
controller.delegate = self
fetch()
}
private func fetch() {
do {
try controller.performFetch()
refresh()
}
catch {
fatalError("Failed to fetch results for \(Result.self)")
}
}
private func refresh() {
results = controller.fetchedObjects?.map { Result($0) } ?? []
}
var predicate: NSPredicate? {
get {
controller.fetchRequest.predicate
}
set {
controller.fetchRequest.predicate = newValue
fetch()
}
}
var sortDescriptors: [NSSortDescriptor] {
get {
controller.fetchRequest.sortDescriptors ?? []
}
set {
controller.fetchRequest.sortDescriptors = newValue.isEmpty ? nil : newValue
fetch()
}
}
internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<any NSFetchRequestResult>) {
refresh()
}
}
Till this point, everything works fine.
Then, I conformed my class to RandomAccessCollection, so I could use in a ForEach loop without having to access the results property.
extension ObservableFetchRequest: @preconcurrency RandomAccessCollection, @preconcurrency MutableCollection {
subscript(position: Index) -> Result {
get {
results[position]
}
set {
results[position] = newValue
}
}
public var endIndex: Index { results.endIndex }
public var indices: Indices { results.indices }
public var startIndex: Index { results.startIndex }
public func distance(from start: Index, to end: Index) -> Int {
results.distance(from: start, to: end)
}
public func index(_ i: Index, offsetBy distance: Int) -> Index {
results.index(i, offsetBy: distance)
}
public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? {
results.index(i, offsetBy: distance, limitedBy: limit)
}
public func index(after i: Index) -> Index {
results.index(after: i)
}
public func index(before i: Index) -> Index {
results.index(before: i)
}
public typealias Element = Result
public typealias Index = Int
}
The issue is, when I update the ObservableFetchRequest predicate while searching, it causes a Index out of range error in the Collection subscript because the ForEach loop (or a List loop) access a old version of the array when the item property is optional.
List(request, selection: $selection) { item in
VStack(alignment: .leading) {
Text(item.content)
if let information = item.information { // here's the issue, if I leave this out, everything works
Text(information)
.font(.callout)
.foregroundStyle(.secondary)
}
}
.tag(item.id)
.contextMenu {
if Item.self is Client.Type {
Button("Editar") {
openWindow(ClientView(client: item as! Client), id: item.id!)
}
}
}
}
Is it some RandomAccessCollection issue or a SwiftUI bug?
We're in the process of migrating our app to the Swift 6 language mode. I have hit a road block that I cannot wrap my head around, and it concerns Core Data and how we work with NSManagedObject instances.
Greatly simplied, our Core Data stack looks like this:
class CoreDataStack {
private let persistentContainer: NSPersistentContainer
var viewContext: NSManagedObjectContext { persistentContainer.viewContext }
}
For accessing the database, we provide Controller classes such as e.g.
class PersonController {
private let coreDataStack: CoreDataStack
func fetchPerson(byName name: String) async throws -> Person? {
try await coreDataStack.viewContext.perform {
let fetchRequest = NSFetchRequest<Person>()
fetchRequest.predicate = NSPredicate(format: "name == %@", name)
return try fetchRequest.execute().first
}
}
}
Our view controllers use such controllers to fetch objects and populate their UI with it:
class MyViewController: UIViewController {
private let chatController: PersonController
private let ageLabel: UILabel
func populateAgeLabel(name: String) {
Task {
let person = try? await chatController.fetchPerson(byName: name)
ageLabel.text = "\(person?.age ?? 0)"
}
}
}
This works very well, and there are no concurrency problems since the managed objects are fetched from the view context and accessed only in the main thread.
When turning on Swift 6 language mode, however, the compiler complains about the line calling the controller method:
Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'fetchPerson(byName:)'
Ok, fair enough, NSManagedObject is not Sendable. No biggie, just add @MainActor to the controller method, so it can be called from view controllers which are also main actor. However, now the compiler shows the same error at the controller method calling viewContext.perform:
Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'perform(schedule:_:)'
And now I'm stumped. Does this mean NSManageObject instances cannot even be returned from calls to NSManagedObjectContext.perform? Ever? Even though in this case, @MainActor matches the context's actor isolation (since it's the view context)?
Of course, in this simple example the controller method could just return the age directly, and more complex scenarios could return Sendable data structures that are instantiated inside the perform closure. But is that really the only legal solution? That would mean a huge refactoring challenge for our app, since we use NSManageObject instances fetched from the view context everywhere. That's what the view context is for, right?
tl;dr: is it possible to return NSManagedObject instances fetched from the view context with Swift 6 strict concurrency enabled, and if so how?
I’m trying to build a CRUD app using SwiftData, @Query model and multidatepicker.
The data from a multidatepicker is stored or persists in SwiftData as Set = [].
My current dilemma is how to use SwiftData and @Query model Predicate to find all records on the current date.
I can’t find any SwiftData documentation or examples @Query using Set = [].
My CRUD app should retrieve all records for the current date. Unfortunately, I don’t know the correct @Query model syntax for Set = [].
The stuff I've found by searching has confused me, so hopefully someone can help simplify it for me?
I have an app (I use it for logging which books I've given away), and I could either add a bunch of things to the app, or I could have another app (possibly a CLI tool) to generate some reports I'd like.
In core-data I have a contact and location entity. I have one-to-many relationship from contact to locations and one-to-one from location to contact. I create contact in a seperate view and save it. Later I create a location, fetch the created contact, and save it while specifying the relationship between location and contact contact and test if it actually did it and it works.
viewContext.perform {
do {
// Set relationship using the generated accessor method
currentContact.addToLocations(location)
try viewContext.save()
print("Saved successfully. Locations count:", currentContact.locations?.count ?? 0)
if let locs = currentContact.locations {
print("📍 Contact has \(locs.count) locations.")
for loc in locs {
print("➡️ Location: \(String(describing: (loc as AnyObject).locationName ?? "Unnamed"))")
}
}
} catch {
print("Failed to save location: \(error.localizedDescription)")
}
}
In my NSManagedObject class properties I have this : for Contact:
@NSManaged public var locations: NSSet?
for Location:
@NSManaged public var contact: Contact?
in my persistenceController I have:
for desc in [publicStore, privateStore] {
desc.setOption(true as NSNumber, forKey:
NSPersistentStoreRemoteChangeNotificationPostOptionKey)
desc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
desc.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: "CKSyncCoreDataDebug") // Optional: Debug sync
// Add these critical options for relationship sync
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitEnforceRecordExistsKey")
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitMaintainReferentialIntegrityKey")
// Add this specific option to force schema update
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreRemoteStoreUseCloudKitSchemaKey")
}
When synchronization happens on CloudKit side, it creates CKRecords: CD_Contact and CD_Location. However for CD_Location it creates the relationship CD_contact as a string and references the CD_Contact. This I thought should have come as REFERENCE On the CD_Contact there is no CD_locations field at all. I do see the relationships being printed on coredata side but it does not come as REFERENCE on cloudkit. Spent over a day on this. Is this normal, what am I doing wrong here? Can someone advise?
Greetings i have an app that uses three different SwiftData models and i want to know what is the best way to use the them accross the app. I though a centralized behaviour and i want to know if it a correct approach.First let's suppose that the first view of the app will load the three models using the @Enviroment that work with @Observation. Then to other views that add data to the swiftModels again with the @Environment. Another View that will use the swiftData models with graph and datas for average and min and max.Is this a corrent way? or i should use @Query in every view that i want and ModelContext when i add the data.
@Observable
class CentralizedDataModels {
var firstDataModel: [FirstDataModel] = []
var secondDataModel: [SecondDataModel] = []
var thirdDataModel: [ThirdDataModel] = []
let context: ModelContext
init(context:ModelContext) {
self.context = context
}
}