Post

Replies

Boosts

Views

Activity

Computed property gets computed infinitely and SwiftUI becomes unresponsive
Hi, I have a computed property in my SwiftUI Todo app that shows the status of the past 4 days - whether the todo was completed or not. For this I have a computed array days that contains the last 4 days including today. Using this array I show the results in checkboxes which is a SwiftUI view. But I have a problem, at midnight when the day changes the app becomes completely unresponsive as this array gets computed again and again infinitely. Only deleting the app and reinstalling it allows it to run normally again, until the next time the day changes when the same thing happens again.var days: [Day] { &#9;&#9;var computedDays = [Day]() let calendar = Calendar.current var dayIndex = calendar.component(.weekday, from: Date()) let lastFourDays = formatDateForPastFourDays(from: Date()) // considering today is 20th August 2020 // lastFourDays = ["August 20, 2020", "August 19, 2020", "August 18, 2020", "August 17, 2020"] let daysInitials = ["Su", "M", "T", "W", "Th", "F", "S"] let checkedDates = computeCheckedDates(lastFourDays) // checkedDates = [true, false] let passedDates = computePassedDates(for: checkedDates, dates: lastFourDays) // passedDates = [false, true, false, false] for idx in 0..<checkedDates.count { computedDays.append(Day(id: idx, date: lastFourDays[idx], dayValue: daysInitials[dayIndex - 1], checked: checkedDates[idx], passed: passedDates[idx])) dayIndex -= 1 if dayIndex == 0 { dayIndex = 7 } } // computedDays = [ToDoApp.Day(id: 0, date: "August 20, 2020", dayValue: "Th", checked: true, passed: false), ToDoApp.Day(id: 1, date: "August 19, 2020", dayValue: "W", checked: false, passed: true)] return computedDays } private func computeCheckedDates(_ dates: [String]) -> [Bool] { &#9;&#9;var checkedDates: [Bool] &#9;&#9;if createdAt != nil { &#9;&#9;&#9;&#9;let calendar = Calendar.current &#9;&#9;&#9;&#9;let daysDiff = calendar.dateComponents([.day], from: calendar.startOfDay(for: createdAt!), to: Date()).day &#9;&#9;&#9;&#9;if daysDiff == 0 { &#9;&#9;&#9;&#9;&#9;&#9;checkedDates = [false] &#9;&#9;&#9;&#9;} else if daysDiff == 1 { &#9;&#9;&#9;&#9;&#9;&#9;checkedDates = [false, false] &#9;&#9;&#9;&#9;} else if daysDiff == 2 { &#9;&#9;&#9;&#9;&#9;&#9;checkedDates = [false, false, false] &#9;&#9;&#9;&#9;} else { &#9;&#9;&#9;&#9;&#9;&#9;checkedDates = [false, false, false, false] &#9;&#9;&#9;&#9;} &#9;&#9;} else { &#9;&#9;&#9;&#9;checkedDates = [false] &#9;&#9;} &#9;&#9;if completedDates != nil { &#9;&#9;&#9;&#9;for idx in 0..<checkedDates.count { &#9;&#9;&#9;&#9;&#9;&#9;checkedDates[idx] = completedDates!.contains(dates[idx]) &#9;&#9;&#9;&#9;} &#9;&#9;} &#9;&#9;return checkedDates } var createdAt: Date? { &#9;&#9;return habit.createdAt ?? Date() } var completedDates: [String]? { &#9;&#9;return habit.completedDates ?? nil } &#9;&#9;&#9;&#9; I think there is a problem in computeCheckedDates method which I haven't been able to figure out because as soon as I return only the current day's checked/unchecked status, the app runs just fine. I am using the days property in a ForEach in SwiftUI and as soon I remove this code, the app also runs completely fine. But with the code I have shared days get computed again and again infinitely as soon as the system date changes to the next day. Any suggestions?
3
0
1.1k
Aug ’20
CoreData does not update all relationships properly
I have two entities in CoreData Todo and DaysOfYear and a many to one relationship exists between these two entities. When the user launches the app if for the past 4 days the user has not completed any of the todos I add those todos to the incompleteTodos relationship for each day. Thing is only the first day gets updated in core data while the rest of them do not update. I have tried everything that I could, all to no avail. Core Data simply refuses to store any of the updates I make except for the first day even though context.hasChanges acknowledges that the context has changed. ContentView from where I add IncompleteTodos: Todo.addIncompleteTodo(todo: todo, for: days[idx].date, context: context) Extension Todo:static func addIncompleteTodo(todo: Todo, for day: String, context: NSManagedObjectContext) { let today = DayOfYear.withDate(convertStringToGMTDate(day), context: context) todo.incompleteDay = today today.addToIncompleteTodos(todo) print(context.hasChanges) try? context.save() } Extension DayOfYear: static func withDate( day: Date, context: NSManagedObjectContext) -> DayOfYear { let request = fetchRequest(NSPredicate(format: "date >= %@ AND date <= %@", day.startOfDay() as CVarArg, day.endOfDay() as CVarArg)) let days = (try? context.fetch(request)) ?? [] if let day = days.first { return day } else { let newDay = DayOfYear(context: context) newDay.date = day return newDay } } static func fetchRequest(_ predicate: NSPredicate) -> NSFetchRequest<DayOfYear> { let request = NSFetchRequest<DaysOfYear>(entityName: "DaysOfYear") request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] request.predicate = predicate return request } So let's say if I add 4 incompleteTodos for 10th, 9th, 8th and 7th September 2020. Core Data would only update the relationship for 7th September and store 4 incompleteTodos while 8th, 9th, 10th Septmeber will show 0 incompleteTodos after running this code. If I add a new Todo on the 10th, it will immediately be reflected in incompleteTodos for the 10th, but will NOT update incompleteTodos for 11th September.
0
0
349
Sep ’20
iOS 14 WidgetKit + Core Data/CloudKit
I am trying to build a widget for iOS 14 using WidgetKit and CoreData/CloudKit instance on a new Xcode 12 project. I have added my CoreData's File Target Membership to both my app and widget I have added an App Group to my main App Target. My Persistence.swift looks like this: init(inMemory: Bool = false) {     container = NSPersistentCloudKitContainer(name: "test-cd")     let storeURL = URL.storeURL(for: "group.test-data", databaseName: "test-cd")     let storeDescription = NSPersistentStoreDescription(url: storeURL)     container.persistentStoreDescriptions = [storeDescription] 4. My widget looks like this: struct Provider: TimelineProvider {   var managedObjectContext : NSManagedObjectContext       init(context : NSManagedObjectContext) {     self.managedObjectContext = context   } ... } @main struct test_widget: Widget {   let kind: String = "test_widget"       var persistentContainer = PersistenceController.shared.container   var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(context: persistentContainer.viewContext)) { entry in       test_widgetEntryView(entry: entry)     }     .configurationDisplayName("My Widget")     .description("This is an example widget.")   } } 5. And my widget's entry view looks like this: struct test_widgetEntryView : View {   @FetchRequest(     sortDescriptors: [NSSortDescriptor(keyPath: \Item.checked, ascending: true)],     animation: .default)   private var items: FetchedResults<Item>       var entry: Provider.Entry   var body: some View {     LazyVStack {       ForEach(items) { item in         HStack { Text("\(item.name)") }       }     }     .padding(.horizontal)   } } But the app crashes on ForEach(items) as soon as it is launched: Thread 1: EXCBADINSTRUCTION (code=EXCI386INVOP, subcode=0x0) Any suggestions as to what is going wrong here?
1
0
1.9k
Sep ’20
How do I update data in my Widget when shared Core Data Container is updated?
Hi, I have a shared Core Data Container between my app and widget. Both have the same app group enabled. I am using data in my widget that accesses data from Core Data and one single image in FileManager. In UIApplication.willResignActiveNotification I reload the timeline of my widget and if I make changes to the "one" single image in my FileManager it is immediately reflected in the updated Widget. But I cant figure out how to fetch updated data from my timeline in the widget from Core Data. I can initially load the data from CD using: struct Provider: TimelineProvider {   var managedObjectContext : NSManagedObjectContext   var items: [Item] = []       init(context : NSManagedObjectContext) {     self.managedObjectContext = context     let fetchRequest = NSFetchRequest<Item>(entityName: "Item")     fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Item.checked, ascending: true), NSSortDescriptor(keyPath: \Item.timestamp, ascending: false)]     do {       items = try managedObjectContext.fetch(fetchRequest)       if items.count == 0 {       }     } catch let error as NSError {       print("Could not fetch: \(error)")     } backgroundImageURL = getDocumentsDirectory().appendingPathComponent("background.png") } ... } But how do I update "items" in getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ())
0
0
619
Oct ’20