Hi,
I'm currently developing a SwiftUI based app with Core Data and CloudKit sync with the NSPersistentCloudKitContainer.
I found different solutions how to toggle CloudKit sync of the Core Data during runtime. The basic idea of these solutions is the following.
instantiate a new NSPersistentCloudKitContainer
set storeDescription.cloudKitContainerOptions = nil
load persistence store
Some solutions recommend to restart the app manually to avoid exactly my problem.
Issues
So far so good. How can I distribute the new viewContext through my app during runtime. In the main App I distributed the viewContext during startup via @Environment(\.managedObjectContext) and it seems not be updated automatically after a reinitialization of NSPersistentCloudKitContainer.
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistence.container.viewContext)
}
}
After deactivating the CloudKit sync I receive the following error when I try to add a new entity.
[error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'TestEntity' so +entity is unable to disambiguate.
Any ideas?
Regards
Sven
Post
Replies
Boosts
Views
Activity
Hi,
I want to activate/deactivate the CloudKit Sync during App runtime in a user settings view. Basically this works fine. Every time I toggle between the NSPersistentContainer and the NSPersistentCloudKitContainer, I increase the persistence.persistenceContainerReloaded attribute and the whole view hierarchy will be reloaded. Thus all changes are passed through the whole app.
During the reload phase I have to load a new persistence store by container.loadPersistentStores(...). Unfortunately, I cannot remove the old persistence store before loading the new one. The app crashes immediately, because the store and viewContext is still in use. Therefore, I just create a new one and trigger the reload. Afterwards every view is using the new viewContext. But somewhere in the background there is still the old persistence store with CloudKit Sync active and pushes every local change to the cloud. Changes on the cloud from other devices are not received anymore.
Does someone has any idea, how to correctly unload a PersistentStore (replace NSPersistentCloudKitContainer by NSPersistentContainer) in a SwiftUI based app?
@main
struct TargetShooterApp: App {
@StateObject var persistence: Persistence = Persistence.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistence.container.viewContext)
.id(persistence.persistenceContainerReloaded)
}
}
}
I'm testing the the new Charts Framework from Apple with Xcode 14.0 Beta 2.
But I face some strange issues when I try to scale the y-axis to some concrete values.
var body: some View {
VStack {
GroupBox("Serien") {
Chart {
ForEach(seriesData) { series in
BarMark(
x: .value("Series", series.name),
y: .value("Wert", series.value)
)
.cornerRadius(8.0)
.annotation(position: .overlay, alignment: .top, spacing: 10.0) {
Text("\(series.value)")
.foregroundColor(.white)
.fontWeight(.bold)
}
}
RuleMark(y: .value("Durchschnitt", shotResult.wrappedSeriesAsInt.mean()))
.foregroundStyle(.red)
}
.chartYScale(domain: minSeriesYAxis...maxSeriesYAxis)
.frame(height: 220, alignment: .topLeading)
}
.backgroundStyle(Color.white)
.shadow(radius: 5.0, x: 2.0, y: 2.0)
}
}
Resulting View
The LineMark or RectangleMark looks ok. But e.g. the AreaMark has the same issue.
I'm trying to dynamically change the sort order to sections SwiftUI list.
The Core Data model consists of an Item Entity with a group and timestamp attribute.
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State var counter: UInt32 = 0
@State var order: SortOrder = .reverse
@SectionedFetchRequest(
sectionIdentifier: \.group!,
sortDescriptors: [SortDescriptor(\.timestamp, order: .reverse)],
predicate: nil,
animation: .default
)
private var items: SectionedFetchResults<String, Item>
var body: some View {
NavigationView {
List {
ForEach(items) { section in
Section(header: Text("\(section.id)")) {
ForEach(section) { item in
NavigationLink {
VStack {
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
Text("\(item.group!)")
}
} label: {
VStack {
Text(item.timestamp!, formatter: itemFormatter)
}
}
}
}
}
}
.navigationTitle("Sectioned List")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
ToolbarItem(placement: .principal) {
Button(action: toggleSortOrder) {
Label("Sort Oder", systemImage: "arrow.up.arrow.down")
}
}
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
newItem.group = "Group #\(counter)"
counter = (counter + 1) % 3
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func toggleSortOrder() {
order = order == .reverse ? .forward : .reverse
items.sortDescriptors = [SortDescriptor(\.timestamp, order: order)]
}
}
As soon as I toggle the sort order, the app crashes with the following error code. The same code with a non-sectioned list works perfectly. In UIKit applications there was a possibility to set tableView.beginUpdates()and tableView.endUpdates(). Is there any similar functions available in SwiftUI?
2022-01-24 22:09:17.323096+0100 SectionedListSortOrder[69887:2007668] *** Assertion failure in -[_UITableViewUpdateSupport _computeRowUpdates], UITableViewSupport.m:568
2022-01-24 22:09:17.347996+0100 SectionedListSortOrder[69887:2007668] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView internal inconsistency: encountered out of bounds global row index while preparing batch updates (oldRow=8, oldGlobalRowCount=8)'