Nick, thanks for the input.Behaviours I've noticed...Scenario 1Existing Core Data app with existing records.Log in to the same Apple ID on different Simulators or Devices.Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = trueOnly new records are synced to users CloudKit account and therefore across devices using the same Apple ID. Existing records remain only on the device on which they are created.Scenario 2Existing Core Data app with existing records.Log in to the same Apple ID on different Simulators or Devices.Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = trueEnable NSPersistentHistoryTrackingKey... guard let containerStoreDescription = container.persistentStoreDescriptions.first else { fatalError("\(#function): Failed to retrieve a persistent store description.") } containerStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)ANY records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account and therefore across devices that are using the same Apple ID.Records entered prior to setting NSPersistentHistoryTrackingKey = true are never synced to users CloudKit account and remain only on the device on which they are created..If NSPersistentCloudKitContainer is switched to NSPersistentContainer, run a few times, then switched back to NSPersistentCloudKitContainer, even the previous records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account... in fact, so long as the CloudKit development environment is not reset, the app can be deleted from all devices and again, previous records entered with NSPersistentHistoryTrackingKey = true are synced to users CloudKit account and therefore across devices. To reiterate, any records entered prior to setting NSPersistentHistoryTrackingKey = true are never synced to users CloudKit account and remain only on the device on which they are created.Note that I have found it is necessary to set NSPersistentHistoryTrackingKey = true when switching from NSPersistentCloudKitContainer back to NSPersistentContainer (only done for testing purposes during my efforts to sync existing records).Scenario 3Existing Core Data app with no existing records.Log in to the same Apple ID on different Simulators or Devices.Enable NSPersistentCloudKitContainer and include container.viewContext.automaticallyMergesChangesFromParent = trueALL records are synced to users CloudKit account and therefore across devices that are using the same Apple ID.Unnecessary to separately enable NSPersistentHistoryTrackingKey.Some developers have suggested some interesting (and functional) workarounds. The most interesting to me is to set a bool for each existing record following the successful load of an NSPersistentCloudKitContainer. Toggling this bool for each record after loading the NSPersistentCloudKitContainer forces a sync for those existing records. While it is the best workaround I have stumbled upon so far, this also seems extremely cumbersome to me and I cannot bring myself to write the code to make this work.Surely there must be a mechanism to apply NSPersistentHistoryTrackingKey = true to all previous records? That is what I will be focussing on next, so any hints or guidance would be gratefully appreciated.
Post
Replies
Boosts
Views
Activity
Have you set automaticallyMergesChangesFromParent = trueI choose to do this after I load my persistent container... lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentCloudKitContainer(name: persistentStoreName) container.viewContext.automaticallyMergesChangesFromParent = true container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } container.viewContext.automaticallyMergesChangesFromParent = true return container }()
Still working on it roel,Although it is a clever workaround, I still cannot accept the bool hack as a reasonable solution.I've logged a feedback report with Apple, no response yet.Currently preparing a Developer TSI on the matter.If I find a solution I'll add a response to this thread.
I'm experiencing the same or similar problem.I suspect, but I am not certain, that it is a bug and I'll try to explain my reasons why... but first...My scenario:Xcode 11.3 project developing for iOS 13.x and beginning to implement NSPersistentCloudKitContainer.I have `Transformable` type attributes for two Entities in my data model (for some time now). Incidentally both these attributes are used to store an optional value for Measurement<UnitMass>, which I understand from my reseach, conforms to `Encodable` and is therefore able to "just work" with a Core Data attribute of type `Transformable`. Others have written more on the capabilities of the Foundation `Measurement` class, using more accurate language than me, in this and other forums, so I'll leave this part of my description short.As I am still in development with my Xcode project, I regularly switch between NSPersistentContainer and NSPersistentCloudKitContainer for testing purposes. This requires regularly resetting the development environment using the CloudKit Dashboard.To ensure my app loads and runs regardless of which type of persistent container I am currently using, I also enable `NSPersistentHistoryTrackingKey = true`. This ensures that records persisted while my project's "container" is of type NSPersistentContainer are also available while my project's "container" is of type NSPersistentCloudKitContainer.I recently updated my .xcdatamodel to add new attributes and new relationships to three entities. Two fo these entities contained the aforementioned attributes used to store optional values for Measurement<UnitMass>. Of these two, both contained renamed attributes and implemented the Renaming ID offered by lightweight migration. I reiterate here for clarity that these renamed attributes were not those used to store optional values for Measurement<UnitMass>, but did belong to the same entity.It seems to me that I was experiencing compounding problems...Intially my errors were related to your first error code: "One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName..." etc.I admit here that I'm not proficient in the use of value transformers or the use of the `Transformable` attribute type... So I hacked away for some time, attempting to apply NSSecureUnarchiveFromData in the Transformer setting in the Attribute Inspector for each of the two attributes that were of type `Transformable`. This didn't seem to have any effect, so I began testing this on other "devices".To make matters more confusing, I was experiencing different errors depending on what testing platform I was using and when I last updated that app's model - so in fact I was experiencing three different errors - Simulator with iPhone 8 Plus running iOS 13.3, Simulator with iPad Pro 12" 3rd generation running iOS 13.3, on device (iPhone 8 Plus) running iOS 13.3. I eventually deleted the instances of the app on the Simulators, but was extremely reluctant to do so on device due to the large amount of test data installed. Yes I am aware that I could download that and reinstall, but honestly I wanted to try to "fix" this problem.One of the errors included commentary on the inability of Core Data to rename attributes that are related to CloudKit records.So my three errors in fact this led me in part to my solution... roll back the data model. I reverted back to an earlier stable data model file that I knew did not cause any errors and ran it succesfully on the previously mentioned devices.Going forward, I didn't attempt to change/update the attribute names as I had previously... for my next change in the data model I added the "new" attributes, then in the subsequent change in the data model I deleted the "old" attribute. Each time I ran the app on all three devices and received no errors. (Note: I accepted some minor data loss in records where old relationships were nullified on deletion.)I reiterate here that all this was completed in a development environment in CloudKit.I regret not recording the exact details of the three different types of errors... but they all began with "One or more models in this application are using transformable properties with transformer names that are either unset, or set to NSKeyedUnarchiveFromDataTransformerName." etc.Hope this essay is of some help.
Helpful suggestion thank you... Maybe you intended to include code for the use of the ViewModifier?
I'm working through this same exercise at the moment...So far I've found this one of the better articles to read - short and concise - https://dev.to/hugh_jeremy/adding-an-nstableview-to-a-swiftui-view-212pSeems we need to create a wrapper with NSViewControllerRepresentableThis is a work-in-progress for me...
Using MBP, macOS 10.15.5, Xcode 11.5 (11E608c).Writing SwiftUI for macOS 10.15 deployment target.SwiftUI, using MapKit with NSViewRepresentable.Also seeing the same error message.(Incidentally also see the same error messages in the macOS app in SwiftUI tutorial Creating a macOS app.Console error... at breakpoint...Calling IOPPFGetProperty thermally_optimized_maps!Then console dumps a huge number of these messages...=== AttributeGraph: cycle detected through attribute xx ===[SwiftUI] NSHostingView is being laid out reentrantly while rendering its SwiftUI content. This is not supported and the current layout pass will be skipped.Issue Navigator under Runtime (only this 1 issue)...runtime: SwiftUI: NSHostingView is being laid out reentrantly while rendering its SwiftUI content. This is not supported and the current layout pass will be skipped.Copied from the Debug Navigator...Thread 7 Queue : com.apple.CoreAnalytics::Client (serial)#0 0x00007fff6f3da1ac in __cxa_begin_catch ()#1 0x00007fff4f962dbf in FrameworkConfiguration::init() ()#2 0x00007fff4f962a98 in FrameworkConfiguration::create(applesauce::xpc::object) ()#3 0x00007fff4f962742 in CoreAnalytics::Client::handleConfigurationChange_sync(applesauce::xpc::dict const&) ()#4 0x00007fff4f962542 in CoreAnalytics::Client::handleServerMessage_sync(applesauce::xpc::dict) ()#5 0x00007fff4f9748cf in ___ZZN13CoreAnalytics6Client4initEvENK3$_1clEv_block_invoke ()#6 0x00007fff722ee2bc in _xpc_connection_call_event_handler ()#7 0x00007fff722ed1cb in _xpc_connection_mach_event ()#8 0x00000001004f28c6 in _dispatch_client_callout4 ()#9 0x000000010050fbcc in _dispatch_mach_msg_invoke ()#10 0x00000001004f9be6 in _dispatch_lane_serial_drain ()#11 0x0000000100510f6c in _dispatch_mach_invoke ()#12 0x00000001004f9be6 in _dispatch_lane_serial_drain ()#13 0x00000001004fab90 in _dispatch_lane_invoke ()#14 0x0000000100507fe0 in _dispatch_workloop_worker_thread ()#15 0x0000000100580361 in _pthread_wqthread ()#16 0x000000010057f49b in start_wqthread ()
Not so much an answer, more a report of where I've got to with this so far... in the hope that it may prompt further discussion and frankly help me find a stable solution.
I've successfully hooked up a Core Data persistent store in the following manner...
@main
struct CoreDataApp: App {
@StateObject var persistentStore = PersistentStore.shared
var body: some Scene {
WindowGroup {
ContentView(store: persistentStore)
.environment(\.managedObjectContext, persistentStore.context)
}
}
}
where the persistent store is...
import Cocoa
import CoreData
class PersistentStore: ObservableObject {
var context: NSManagedObjectContext { persistentContainer.viewContext }
		static let shared = PersistentStore()
private init() {}
private let persistentStoreName: String = "TheNameOfMySQLiteStore"
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
		// ... per Apple's Core Data stack persistent container template ...
		}
}
(Acknowledgement to KrakenDev for the one line singleton.)
and then I carry the reference to the PersistentStore into the ContentView as an @ObservedObject...
struct ContentView: View {
@ObservedObject var store: PersistentStore
		...etc.
}
The most important part of this process IMHO is the injection of the NSManagedObjectContext (viewContext) from a single instance of the NSPersistentContainer into each and any view that requires access to the context. NSManagedObjects by default adopt the ObservableObject protocol so I don't need to apply this to each Core Data subclassed NSManagedObject entities. Although for the use in List, it is worth adopting the Identifiable protocol. Any change or addition or deletion to an NSManagedObject within the context therefore remains in the context, until such time that the App closes. If at any stage I call "do try context.save()", then any create, update or delete will persist.
My advice - don't focus on @ObservableObject and @ObservedObject - focus on injecting an NSManagedObjectContext into the views where you need to create, read, update or delete.
So exactly what happens when the App closes is not clear to me and is possibly why Apple still recommend the use of AppDelegate in it's SwiftUI Core Data template, because it provides API such as...
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {}
... which helps us manage Core Data related activity.
I'm finding it relatively easy to implement CRUD processes. I'm using @FetchRequest to grab data from the @Environment(\.managedObjectContext) var context that I include in each View struct as required, and I've begun to experiment with dynamic fetch requests that take a generic NSManagedObject.
What I'm finding extremely difficult is the process of maintaining stable views using the new App & Scene protocols and WindowGroup container. I'm putting that down to two things...
my lack of knowledge/understanding;
if the new API were ready for Core Data, then Xcode would provide a template to use these with Core Data.
Which leads me to guess that AppDelegate instantiated apps with Core Data may be the safest path for the immediate future.
Not that I'm heading down that path yet!
I found a much better implementation of Core Data with new App and Scene protocols that provides access to change of app state through the .onChange(of: scenePhase) modifier.
I'd recommend using that.
Search Apple Developer Forums for "Using Core Data with SwiftUI App Protocol" under tag wwdc20-10041
Here is the thread link... look to the accepted answer...
https://developer.apple.com/forums/thread/650876
@micho2, to achieve CloudKit integration you should be able to change the persistentContainer property like so...
var persistentContainer: NSPersistentContainer = {
				let container = NSPersistentCloudKitContainer(name: "SampleApp") 						 // change this line
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
								fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
				container.viewContext.automaticallyMergesChangesFromParent = true								 // add this line
return container
}()
although I've not tested this yet!
I have it working in macOS 11 beta. I admit that it took some time for me to find the correct location to apply the .toolbar modifier though.
It needs to be applied as a modifier to a View that is contained within a NavigationView.
I'm currently using this code within the .toolbar modifier for a view that is embedded within a NavigationView...
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: appData.isEditing.toggle()) {
Label(appData.isEditing ? "Done" : "Edit",
systemImage: appData.isEditing ? "pencil.circle.fill" : "pencil.circle")
}
.disabled(<< insert your own logic here >>)
.help("Edit the selected record")
}
}
and this is my AppData class that conforms to ObservableObject that I use to store the value for isEditing...
final class AppData: ObservableObject {
@Published var isEditing: Bool = false
}
Alternatively you could use the @State property wrapper within your View struct that contains the NavigationView...
struct yourView: View {
		@State private var isEditing = false
		...
}
I haven't attempted to use the Toggle View before, but to answer this question I just used it successfully in a macOS target I'm writing in SwiftUI...
Toggle(isOn: $appData.isEditing) {
Label(appData.isEditing ? "Done" : "Edit",
systemImage: appData.isEditing ? "pencil.circle.fill" : "pencil.circle")
}
.disabled(<< insert your own logic here >>)
.help("Edit the selected record")
The benefit of using Toggle over Button, is that when the toggle value for isOn = true, the Toggle has a slightly darker background to demonstrate the selected state. This would have to be added manually to Button to achieve the same effect.
I'll be using Toggle.
So for any passers-by that are interested in how I eventually achieved this...
(Credit to mtsrodrigues for how to deal with change of Scene in his answer in this thread... https://developer.apple.com/forums/thread/650876)
My Core Data implementation for SwiftUI and the new App and Scene protocols and Window container!
My SwiftUI App...
struct MyApp: App {
@Environment(\.scenePhase) private var scenePhase
@StateObject private var persistentStore = PersistentStore.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistentStore.context)
}
.onChange(of: scenePhase) { phase in
switch phase {
case .active:
print("\(#function) REPORTS - App change of scenePhase to ACTIVE")
case .inactive:
print("\(#function) REPORTS - App change of scenePhase to INACTIVE")
case .background:
print("\(#function) REPORTS - App change of scenePhase to BACKGROUND")
savePersistentStore()
@unknown default:
fatalError("\(#function) REPORTS - fatal error in switch statement for .onChange modifier")
}
}
}
func savePersistentStore() {
persistentStore.save()
}
}
and my custom class for PersistentStore (noting the one line singleton as documented by KrakenDev)...
import SwiftUI
import CoreData
class PersistentStore: ObservableObject {
var context: NSManagedObjectContext { persistentContainer.viewContext }
		
		// One line singleton
static let shared = PersistentStore()
		
		// Mark the class private so that it is only accessible through the singleton `shared` static property
private init() {}
private let persistentStoreName: String = "YourPersistentStoreName"
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: persistentStoreName)
				
// Enable history tracking
// (to facilitate previous NSPersistentCloudKitContainer to load as NSPersistentContainer)
// (not required when using NSPersistentCloudKitContainer)
guard let persistentStoreDescriptions = container.persistentStoreDescriptions.first else {
fatalError("\(#function): Failed to retrieve a persistent store description.")
}
persistentStoreDescriptions.setOption(true as NSNumber,
forKey: NSPersistentHistoryTrackingKey)
persistentStoreDescriptions.setOption(true as NSNumber,
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
				// Add the following line to set your merge policy...
				container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}()
// MARK: - Core Data Saving and Undo support
func save() {
let context = persistentContainer.viewContext
if !context.commitEditing() {
NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving")
}
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
NSApplication.shared.presentError(nserror)
}
}
}
}
I recommend the first three as mandatory...
Paul Hegarty's Stanford course is excellent CS193p Developing Apps for iOS - cs193p.sites.stanford.edu
The Apple Developer SwiftUI tutorial is the best learning tool Apple has ever released - developer.apple.com/tutorials/swiftui/
Watch ALL the WWDC videos - I try one a day - start with Swift, SwiftUI and Combine
Then refer to these websites for good reliable well written articles:
Others have recommended Swift by Sundell, and so do I - swiftbysundell.com
NSHipster for many excellent articles on Swift - nshipster.com
For some more in-depth articles on certain aspects of SwiftUI, try: The SwiftUI-Lab - swiftui-lab.com Swift with Majid - swiftwithmajid.com
My two cents...
My Core Data implementation for SwiftUI using the new App and Scene protocols and Window container!
Credit to: mtsrodrigues for how to deal with change of Scene in his answer in this thread.
KrakenDev's article on how to write a one line singleton
My SwiftUI App...
import SwiftUI
@main
struct YourApp: App {
@Environment(\.scenePhase) private var scenePhase
@StateObject private var persistentStore = PersistentStore.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistentStore.context)
}
.onChange(of: scenePhase) { phase in
switch phase {
case .active:
print("\(#function) REPORTS - App change of scenePhase to ACTIVE")
case .inactive:
print("\(#function) REPORTS - App change of scenePhase to INACTIVE")
case .background:
print("\(#function) REPORTS - App change of scenePhase to BACKGROUND")
savePersistentStore()
@unknown default:
fatalError("\(#function) REPORTS - fatal error in switch statement for .onChange modifier")
}
}
}
func savePersistentStore() {
persistentStore.save()
}
}
My "Core Data stack" - essentially Apple's template code embedded within a custom class that conforms to ObservableObject.
import SwiftUI
import CoreData
class PersistentStore: ObservableObject {
var context: NSManagedObjectContext { persistentContainer.viewContext }
		// One line singleton
static let shared = PersistentStore()
		// Mark the class private so that it is only accessible through the singleton `shared` static property
private init() {}
private let persistentStoreName: String = "YourPersistentStoreName"
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: persistentStoreName)
				// OR - Include the following line for use with CloudKit - NSPersistentCloudKitContainer
// let container = NSPersistentCloudKitContainer(name: persistentStoreName)
// Enable history tracking
// (to facilitate previous NSPersistentCloudKitContainer's to load as NSPersistentContainer's)
// (not required when only using NSPersistentCloudKitContainer)
guard let persistentStoreDescriptions = container.persistentStoreDescriptions.first else {
fatalError("\(#function): Failed to retrieve a persistent store description.")
}
persistentStoreDescriptions.setOption(true as NSNumber,
forKey: NSPersistentHistoryTrackingKey)
persistentStoreDescriptions.setOption(true as NSNumber,
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
// Replace this implementation with code to handle the error appropriately.
fatalError("Unresolved error \(error)")
}
})
				// Include the following line for use with CloudKit - NSPersistentCloudKitContainer
container.viewContext.automaticallyMergesChangesFromParent = true
				// Include the following line for use with CloudKit and to set your merge policy, for example...
				container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}()
// MARK: - Core Data Saving and "other future" support (such as undo)
func save() {
let context = persistentContainer.viewContext
if !context.commitEditing() {
NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving")
}
if context.hasChanges {
do {
try context.save()
} catch {
// Customize this code block to include application-specific recovery steps.
let nserror = error as NSError
NSApplication.shared.presentError(nserror)
}
}
}
}
To use the context within a Preview...
struct YourSwiftUIStruct_Previews: PreviewProvider {
static var previews: some View {
let context = PersistentStore.shared.context
return YourSwiftUIStruct()
.environment(\.managedObjectContext, context)
}
}