I have started from here: Apple's guide on the sharing core data objects between iCloud users and I have created a sample project that has Collections and Items. Everything works great while I stay on Swift 5, like with the initial project.
I would like to migrate to Swift 6 (Default Actor Isolaton @MainActor, Approachable Concurrency: Yes) on the project and I am stuck at extension CDCollection: Transferable { ... }. When compiling with Swift 5, there is a warning: Conformance of 'NSManagedObject' to 'Sendable' is unavailable in iOS; this is an error in the Swift 6 language mode.
After resolving almost all compile-time warnings I'm left with:
Conformance of 'CDCollection' to protocol 'Transferable' crosses into main actor-isolated code and can cause data races.
Which I don't think will work, because of the warning shown above. It can be worked around like:
nonisolated extension CDCollection: Transferable, @unchecked Sendable
Then there are errors:
let persistentContainer = PersistenceController.shared.persistentContainer Main actor-isolated static property 'shared' can not be referenced from a nonisolated context.
I've created the following class to have a Sendable object:
struct CDCollectionTransferable: Transferable {
var objectID: NSManagedObjectID
var persistentContainer: NSPersistentCloudKitContainer
public static var transferRepresentation: some TransferRepresentation {
CKShareTransferRepresentation { collectionToExport in
let persistentContainer = collectionToExport.persistentContainer
let ckContainer = CloudKitProvider.container
var collectionShare: CKShare?
if let shareSet = try? persistentContainer.fetchShares(
matching: [collectionToExport.objectID]),
let (_, share) = shareSet.first
{
collectionShare = share
}
/**
Return the existing share if the collection already has a share.
*/
if let share = collectionShare {
return .existing(share, container: ckContainer)
}
/**
Otherwise, create a new share for the collection and return it.
Use uriRepresentation of the object in the Sendable closure.
*/
let collectionURI = collectionToExport.objectID
.uriRepresentation()
return .prepareShare(container: ckContainer) {
let collection = await persistentContainer.viewContext
.perform {
let coordinator = persistentContainer.viewContext
.persistentStoreCoordinator
guard
let objectID = coordinator?.managedObjectID(
forURIRepresentation: collectionURI
)
else {
fatalError(
"Failed to return the managed objectID for: \(collectionURI)."
)
}
return persistentContainer.viewContext.object(
with: objectID
)
}
let (_, share, _) = try await persistentContainer.share(
[collection],
to: nil
)
return share
}
}
}
}
And I'm able to compile and run the app with this change:
let transferable = CDCollectionTransferable(
objectID: collection.objectID,
persistentContainer: PersistenceController.shared
.persistentContainer
)
ToolbarItem {
ShareLink(
item: transferable,
preview: SharePreview("Share \(collection.name)!")
) {
MenuButtonLabel(
title: "New Share",
systemImage: "square.and.arrow.up"
)
}
}
The app crashes when launched with
libdispatch.dylib`_dispatch_assert_queue_fail:
0x1052c6ea4 <+0>: sub sp, sp, #0x50
0x1052c6ea8 <+4>: stp x20, x19, [sp, #0x30]
0x1052c6eac <+8>: stp x29, x30, [sp, #0x40]
0x1052c6eb0 <+12>: add x29, sp, #0x40
0x1052c6eb4 <+16>: adrp x8, 63
0x1052c6eb8 <+20>: add x8, x8, #0xa0c ; "not "
0x1052c6ebc <+24>: adrp x9, 62
0x1052c6ec0 <+28>: add x9, x9, #0x1e5 ; ""
0x1052c6ec4 <+32>: stur xzr, [x29, #-0x18]
0x1052c6ec8 <+36>: cmp w1, #0x0
0x1052c6ecc <+40>: csel x8, x9, x8, ne
0x1052c6ed0 <+44>: ldr x10, [x0, #0x48]
0x1052c6ed4 <+48>: cmp x10, #0x0
0x1052c6ed8 <+52>: csel x9, x9, x10, eq
0x1052c6edc <+56>: stp x9, x0, [sp, #0x10]
0x1052c6ee0 <+60>: adrp x9, 63
0x1052c6ee4 <+64>: add x9, x9, #0x9db ; "BUG IN CLIENT OF LIBDISPATCH: Assertion failed: "
0x1052c6ee8 <+68>: stp x9, x8, [sp]
0x1052c6eec <+72>: adrp x1, 63
0x1052c6ef0 <+76>: add x1, x1, #0x9a6 ; "%sBlock was %sexpected to execute on queue [%s (%p)]"
0x1052c6ef4 <+80>: sub x0, x29, #0x18
0x1052c6ef8 <+84>: bl 0x105301b18 ; symbol stub for: asprintf
0x1052c6efc <+88>: ldur x19, [x29, #-0x18]
0x1052c6f00 <+92>: str x19, [sp]
0x1052c6f04 <+96>: adrp x0, 63
0x1052c6f08 <+100>: add x0, x0, #0xa11 ; "%s"
0x1052c6f0c <+104>: bl 0x1052f9ef8 ; _dispatch_log
0x1052c6f10 <+108>: adrp x8, 95
0x1052c6f14 <+112>: str x19, [x8, #0x1f0]
-> 0x1052c6f18 <+116>: brk #0x1
The app still crashes when I comment this code, and all Core Data related warnings.
I'm quite stuck now as I want to use Swift 6.
Has anyone figured CloudKit, CoreData and Swift 6 for sharing between users?