SwiftUI Delete a List Section - xcode

Is there a way to delete an entire list section with a function in SwiftUI? I have a list that you can add sections to as you please, but don't know how to delete them when you want to. I know that you can delete a list item itself using the deleteItems function, but I would like to use a button to delete the entire section. When I try to use the standard "deleteItems" function, it asks for "offsets: IndexSet". I am unsure what this IndexSet would be.
I can't find out what should go in the IndexSet field.
Here is my code
struct SampleView: View {
#Environment(\.managedObjectContext) private var viewContext
#Environment(\.presentationMode) var presentationMode
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Category.categoryString, ascending: false)],
animation: .default)
private var listedCategory: FetchedResults<Category>
#State var categoryString: String = ""
var body: some View {
VStack{
TextField("Enter Text Here", text: $categoryString)
Button {
let newlistedCategory = Category(context: viewContext)
newlistedCategory.categoryString = categoryString
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
categoryString = ""
} label: {
Text("Save")
}
List{
ForEach(listedCategory) { listedCategory in
if listedCategory.typeString == "Item" {
Section(header: Text(listedCategory.categoryString!)) {
Button {
//deleteCategories(offsets: <#T##IndexSet#>)
} label: {
Text("Delete")
}
}
}
}
}
Spacer()
}
}
private func deleteCategories(offsets: IndexSet) {
withAnimation {
offsets.map { listedCategory[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}

Related

Unable to place Bool value in a toggle in swiftui

Hey all I have been trying to fix this issue for 2 days now and just can't seem to get what I am lookinhg for.
My working code:
struct User: Decodable {
let name: String
let description: String
let isOn: Bool
}
struct ContentView: View {
#State var users = [User]()
var body: some View {
List{
ForEach(users, id: \.name) { item in
HStack {
Toggle(isOn: .constant(true)) {
Label {
Text(item.description)
} icon: {
//list.img
}
}
}.padding(7)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://-----.---/jsontest.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
the Json its pulling looks like this:
{
"name": "J",
"description": "Echo Show",
"isOn": true
},...
Like I said above this code works as-is. Where the problem I am having comes into play is the toggle part. It doesn't seem to be happens with the item.isOn for seeing if its true or false. The only thing it likes is the .constant(). Naturally this will not work for my case due to me needing to change the toggles to either true or false.
If it's anything other than .constant it has the error of:
Cannot convert value of type 'Bool' to expected argument type 'Binding'
I can perhaps if I do this
#State var ison = false
var body: some View {
List{
ForEach(users, id: \.name) { item in
HStack {
Toggle(isOn: $ison)) {
That seems to take that error away.... but how can it get the value thats looping for isOn that way?
What am I missing here?
try this approach, making isOn a var in User, and using bindings, the $ in the ForEach and Toggle, works for me:
struct User: Decodable {
let name: String
let description: String
var isOn: Bool // <-- here
}
struct ContentView: View {
#State var users = [User]()
var body: some View {
List{
ForEach($users, id: \.name) { $item in // <-- here
HStack {
Toggle(isOn: $item.isOn) { // <-- here
Label {
Text(item.description)
} icon: {
//list.img
}
}
}.padding(7)
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://-----.---/jsontest.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}

SwiftUI: Sharing NSSharingService on macOS not receiving share

I have a simple test application that attempts to share a CoreData record with another user using NSSharingService. I can create my share and that works,
but when I try to receive the share it opens up the application but doesn't do anything.
I have added CKSharingSupported to my plist.
I have also followed this link to no avail:
CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App
Here is my code:
SharingServiceApp:
final class AppDelegate: NSObject, NSApplicationDelegate
{
func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShare.Metadata)
{
print ("userDidAcceptCloudKitShareWith")
let shareStore = persistenceController.sharedPersistentStore!
persistenceController.container.acceptShareInvitations(from: [metadata], into: shareStore)
{ _, error in
if let error = error
{
print("acceptShareInvitation error :\(error)")
}
}
}
}
ContentView:
import SwiftUI
import CoreData
import CloudKit
let persistenceController = PersistenceController()
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink {
VStack
{
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
Button("Share")
{
shareRecord(item: item)
}
}
} label: {
Text(item.timestamp!, formatter: itemFormatter)
}
}
}
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
func shareRecord(item: Item)
{
Task
{
if let share = await createShare(item: item)
{
let container = CKContainer(identifier: "iCloud.com.xxxxxxxx.sharingservice")
let item = NSItemProvider()
item.registerCloudKitShare(share, container: container)
DispatchQueue.main.async
{
let sharingService = NSSharingService(named: .cloudSharing)
sharingService!.perform(withItems: [item])
}
}
}
}
private func createShare(item: Item) async -> CKShare?
{
do
{
let (_, share, _) = try await persistenceController.container.share([item], to: nil)
share[CKShare.SystemFieldKey.title] = "MyApp"
return share
}
catch
{
print("Failed to create share")
return nil
}
}
OK I have finally managed to get userDidAcceptCloudKitShareWith to be called.
You need to create the app delegate for your SwiftUI app using #NSApplicationDelegateAdaptor:
#main
struct Sharing_ServiceApp: App
{
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
{
WindowGroup
{
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
I put that line in and my code instantly started receiving the share requests.

SwiftUI(Core Data) - Xcode Preview stops, but does not crash

Although Xcode does not give me any information, I assumed that it is related to Core Data because it started since I changed codes related to Core Data.
My Code
I have many codes in my project, and I cannot show you the whole code because of word limit. But I can show the core of my project.
ContentView - includes two tab views(VocabularyFolderView and QuizHome View)
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
VocabularyFolderView()
.tabItem {
Label("Vocabulary", systemImage: "list.dash")
}
QuizHomeView()
.tabItem {
Label("Quiz", systemImage: "questionmark.app")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
//ForEach(["iPhone SE (3nd generation)", "iPhone 13", "iPad Pro (11-inch) (1st generation)", "iPad Air (3rd generation)", "iPad Air (4th generation)"], id: \.self) { deviceName in
//ContentView()
//.environment(\.managedObjectContext, PersistenceController.VocabularyFolder_Preview.container.viewContext)
//.previewDevice(PreviewDevice(rawValue: deviceName))
//.previewDisplayName(deviceName)
//}
ContentView()
.environment(\.managedObjectContext, PersistenceController.VocabularyFolder_Preview.container.viewContext)
.previewInterfaceOrientation(.portrait)
}
}
VocabularyFolderView - It is a NavigationView that contains a list of Core Data entities
import SwiftUI
import CoreData
struct VocabularyFolderView: View {
#Environment(\.managedObjectContext) private var managedObjectContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \FolderStruct.name, ascending: true)],
animation: .default)
private var folders: FetchedResults<FolderStruct>
#State private var showAddView: Bool = false
#State private var showAddErrorAlert: Bool = false
#State private var showDeleteErrorAlert: Bool = false
#State private var showFavoritesOnly = false
var filteredFolders: [FolderStruct] {
folders.filter { folder in
(!showFavoritesOnly || folder.starred)
}
}
var body: some View {
NavigationView {
List {
Section {
Toggle(isOn: $showFavoritesOnly) {
Text("Favorites only")
}
}
Section {
ForEach(filteredFolders) { folder in
NavigationLink {
VocabularyListView(parentFolder: folder)
} label: {
HStack {
Image(systemName: "folder")
.foregroundColor(.accentColor)
.listRowSeparator(.hidden)
Text(folder.name ?? "")
Spacer()
Text("\(folder.childlists!.count)")
.foregroundColor(.secondary)
if folder.starred {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
} else {
Image(systemName: "star")
.foregroundColor(.gray)
}
}
}
.swipeActions(edge: .leading) {
Button {
deleteFolder(folder: folder)
} label: {
Label("Pin", systemImage: "pin.fill")
}
.tint(.orange)
}
.swipeActions(edge:. trailing, allowsFullSwipe: false) {
Button(role: .destructive) {
deleteFolder(folder: folder)
} label: {
Label("Delete", systemImage: "trash.fill")
}
}
}
}
}
.navigationTitle("Folders")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem(placement: .bottomBar) {
HStack {
Button(action: addItem) {
Label("Add Item", systemImage: "folder.badge.plus")
}
Spacer()
}
}
}
.sheet(isPresented: $showAddView) {
AddFolderView()
}
//alert(isPresented: $showAddView,
// TextAlert(title: "New Folder", message: "Message") { result in
// if let text = result {
// // Text was accepted
// } else {
// // The dialog was cancelled
// }
// })
.alert("Error while deleting a word", isPresented: $showDeleteErrorAlert) {
Button("OK", role: .cancel) { }
}
.alert("Error while adding a word", isPresented: $showAddErrorAlert) {
Button("OK", role: .cancel) { }
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
private func addItem() {
showAddView = true
}
private func deleteFolder(folder: FolderStruct) {
managedObjectContext.delete(folder)
do {
try managedObjectContext.save()
} catch {
showDeleteErrorAlert = true
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
struct VocabularyFolderView_Previews: PreviewProvider {
static var previews: some View {
VocabularyFolderView().environment(\.managedObjectContext, PersistenceController.VocabularyFolder_Preview.container.viewContext)
}
}
QuizHomeView
import SwiftUI
import CoreData
struct QuizHomeView: View {
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \FolderStruct.name, ascending: true)],
animation: .default)
private var folders: FetchedResults<FolderStruct>
#State private var showingSheet = false
#State private var quizType = "단어 뜻 맞추기"
#State private var folderSelection: FolderStruct?
#State private var listSelection = "단어장 선택"
let quizTypes = ["단어 뜻 맞추기", "원래 단어 맞추기"]
var body: some View {
NavigationView {
Form {
Section {
Picker("Quiz Type", selection: $quizType) {
ForEach(quizTypes, id: \.self) {
Text($0)
}
}
.pickerStyle(.segmented)
}
Section {
Picker("폴더 선택", selection: $folderSelection) {
ForEach(folders) { (folder: FolderStruct) in
Text(folder.name!)
}
}
//Picker("단어장 선택", selection: $listSelection) {
// ForEach(listSelections, id: \.self) {
// Text($0)
// }
//}
}
Section {
Button("시작하기") {
showingSheet.toggle()
}
.disabled(folderSelection == nil || listSelection.isEmpty || listSelection == "단어장 선택")
}
}
.navigationTitle("퀴즈")
.sheet(isPresented: $showingSheet) {
QuizPlayView()
}
}
}
}
struct QuizHomeView_Previews: PreviewProvider {
static var previews: some View {
QuizHomeView()
}
}
Persistence.swift
import CoreData
import Foundation
struct PersistenceController {
static let shared = PersistenceController()
static var VocabularyFolder_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
var previewString = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
for i in previewString {
let newFolder = FolderStruct(context: viewContext)
newFolder.name = i
newFolder.starred = true
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var AddFolder_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let newFolder = FolderStruct(context: viewContext)
newFolder.name = "Example Folder"
newFolder.starred = true
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var VocabularyList_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
var previewString = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
for i in previewString {
let parentFolder = FolderStruct(context: viewContext)
parentFolder.name = i
parentFolder.starred = true
let newList = ListStruct(context: viewContext)
newList.name = i
newList.starred = true
newList.parentfolder = parentFolder
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var AddList_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let parentFolder = FolderStruct(context: viewContext)
parentFolder.name = "Example Folder"
parentFolder.starred = true
let newList = ListStruct(context: viewContext)
newList.name = "Example List"
newList.starred = true
newList.parentfolder = parentFolder
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var Vocabulary_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let previewString = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
for i in previewString {
let parentList = ListStruct(context: viewContext)
parentList.name = i
parentList.starred = true
let newVocabulary = VocabularyStruct(context: viewContext)
newVocabulary.word = i
newVocabulary.meaning = i
newVocabulary.starred = true
newVocabulary.parentlist = parentList
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var AddVocabulary_Preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let parentList = ListStruct(context: viewContext)
parentList.name = "Example List"
parentList.starred = true
let newVocabulary = VocabularyStruct(context: viewContext)
newVocabulary.word = "Example Word"
newVocabulary.meaning = "Example Meaning"
newVocabulary.starred = true
newVocabulary.parentlist = parentList
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
static var FolderStruct_Preview: FolderStruct {
let folder = FolderStruct()
folder.name = "Example Folder"
folder.starred = true
return folder
}
static var ListStruct_Preview: ListStruct {
let list = ListStruct()
list.name = "Example List"
list.starred = true
list.parentfolder = FolderStruct_Preview
return list
}
static var VocabularyStruct_Preview: VocabularyStruct {
let vocabulary = VocabularyStruct()
vocabulary.word = "Example Word"
vocabulary.meaning = "Example Meaning"
vocabulary.starred = true
vocabulary.parentlist = ListStruct_Preview
return vocabulary
}
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "Vocabulary")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}

SwiftUI CoreData Preview FetchedResults Crashing

I am having issues previewing my SwiftUI CoreData in the canvas. The app works as expected in the simulator and on device, but the preview is crashing any preview view that uses a FetchedResult or ObservedObject (which I may be using wrong?).
My Persistence is set up as such, using a preview controller with supplied preview data:
import CoreData
import CloudKit
struct PersistenceController {
//MARK: - 1. PERSISTENCE CONTROLLER
static let shared = PersistenceController()
//MARK: - 2. PERSISTENT CONTAINER
let container: NSPersistentContainer
//MARK: - 3. INIT (load the persistent store)
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "Training")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
//MARK: - 4. PREVIEW
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let newGoalView = GoalViews(context: viewContext)
newGoalView.startDate = K.init().EndOfWeek(weeksSinceAnchorDate: 1)
newGoalView.endDate = K.init().EndOfWeek(weeksSinceAnchorDate: 2)
newGoalView.id = UUID()
newGoalView.totalDistanceUnit = "meters"
newGoalView.totalDistanceDoubleValue = 30000
newGoalView.workoutActivityType = Int16(32)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo), The database failed to load.")
}
return result
}()
}
I am having issues both previewing it in the parent view here:
//
// GoalsView.swift
// Training
//
// Created by Liam Day on 03/05/2021.
//
import SwiftUI
import HealthKit
struct GoalsView: View {
//MARK: - PROPERTIES
#State var showNewGoalView: Bool = false
//MARK: - FETCHING DATA
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \GoalViews.endDate, ascending: true)],
animation: .default) var goalViews: FetchedResults<GoalViews>
func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { goalViews[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
//MARK: - BODY
var body: some View {
NavigationView {
List {
ForEach(goalViews) { goalView in
GoalViewListItemView(goalView: goalView)
}
.onDelete(perform: deleteItems)
}
.listStyle(PlainListStyle())
.navigationBarTitle(Text("Goals"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
self.showNewGoalView = true
}, label: {
Text("Add Goal")
})
}
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
.sheet(isPresented: $showNewGoalView, content: {
CreateGoalView(isShowing: $showNewGoalView)
})
}
}
}
struct GoalsView_Previews: PreviewProvider {
static var previews: some View {
return GoalsView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
And in the individual child view, GoalViewListItemView:
//
// GoalViewListItemView.swift
// Training
//
// Created by Liam Day on 30/05/2021.
//
import SwiftUI
import HealthKit
import CoreData
struct GoalViewListItemView: View {
var goalView: FetchedResults<GoalViews>.Element // changed from observed object
#State var progressValue: Double = 0
#AppStorage("distanceUnitSystemMetric") var distanceUnitSystemMetric: Bool = true
func calcGoalTotal() {
...
}
var body: some View {
NavigationLink(destination: GoalViewDetailView(goalView: goalView, progressValue: $progressValue)) {
VStack(alignment: .leading) {
Text(HKWorkoutActivityType.init(rawValue: UInt(goalView.workoutActivityType))!.commonName)
.font(.title)
Text(K.goalUnit.init(rawValue: (goalView.goalUnit ?? String("totalDistanceDoubleValue")))?.name ?? "Unkown")
Text("Start: \(Bundle.main.getDynamicDayName(date: goalView.startDate ?? Date()))")
Text("Finish: \(Bundle.main.getDynamicDayName(date: goalView.endDate ?? Date()))")
if goalView.goalUnit == K.goalUnit.totalDistanceDoubleValue.rawValue {
Text("Goal of \(Bundle.main.getDistance(totalDistanceDoubleValue: goalView.totalDistanceDoubleValue, totalDistanceUnit: HKUnit.meter().unitString, returnUnit: distanceUnitSystemMetric ? K.init().kilometer : HKUnit.mile().unitString, significantDigits: 3))")
ProgressView(
"\(Bundle.main.getDistance(totalDistanceDoubleValue: progressValue, totalDistanceUnit: HKUnit.meter().unitString, returnUnit: distanceUnitSystemMetric ? K.init().kilometer : HKUnit.mile().unitString, significantDigits: 3))",
value: (progressValue / goalView.totalDistanceDoubleValue),
total: 1
)
} else if goalView.goalUnit == K.goalUnit.totalEnergyBurnedDoubleValue.rawValue {
Text("Goal of \(String(format: "%.0f", goalView.totalEnergyBurnedDoubleValue)) KCal")
ProgressView(
"\(String(format: "%.0f", progressValue)) KCal",
value: (progressValue / goalView.totalEnergyBurnedDoubleValue),
total: 1
)
} else if goalView.goalUnit == K.goalUnit.duration.rawValue {
Text("Goal of \(Bundle.main.getHoursMinutesSeconds(timeInSeconds: goalView.duration))")
ProgressView(
"\(Bundle.main.getHoursMinutesSeconds(timeInSeconds: progressValue))",
value: (progressValue / goalView.duration),
total: 1
)
} else {
EmptyView()
}
}
}
.onAppear(perform: {
calcGoalTotal()
})
}
}
struct GoalViewListItemView_Previews: PreviewProvider {
static var previews: some View {
let viewContext = PersistenceController.preview.container.viewContext
let newGoalView = GoalViews(context: viewContext)
return GoalViewListItemView(goalView: newGoalView)
}
}
I have already swapped the child view from an #ObservedObject with type GoalViews to a FetchedResults<GoalViews>.Element.
The app continues to function as expected as I swap and change FetchedResults, ObservedObjects and States at different levels.
What is the correct data flow from parent (fetching data) to child (needing to access update/delete data)? How are these outlined in the preview if using a preview Persistence Controller?

Unmodified SwiftUI / Core Data default app code does not run in simulator?

When I create and run a new, unmodified, Core Data / SwiftUI app project in Xcode (12.3), the simulator shows a blank screen. Code below.
Upon creation of the new app project, SwiftUI code is generated that includes a List along with Add and Edit buttons. The UI displays correctly in Xcode's preview but not in the simulator.
This is the default code in the ContentView.swift file:
import SwiftUI
import CoreData
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
List {
ForEach(items) { item in
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
}
.onDelete(perform: deleteItems)
}
.toolbar {
#if os(iOS)
EditButton()
#endif
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
// Replace this...
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this...
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
You can reproduce this simply by creating a new iOS App project with SwiftUI and Core Data enabled:
Trying to wrap my head around this stuff. Getting stuck on the default code is not promising!
IMHO Yes this seems like a bug with the example, might be worth submitting with a feedback
Problem:
Since the view is not inside the navigation view the navigation buttons are not visible
The tool bar modifier requires to use the ToolbarItem which is missing.
Overview:
Please wrap the view contents inside a NavigationView
Wrap the tool bar buttons inside ToolbarItem
Solution:
Replace the body computed property as follows:
var body: some View {
NavigationView {
List {
ForEach(items) { item in
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
}
.onDelete(perform: deleteItems)
}
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem(placement: .navigationBarLeading) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
}
}

Resources