Home > Blockchain >  shape data from core data into plottable format
shape data from core data into plottable format

Time:09-16

I have a conceptual question regarding plotting charts in SwiftUI based on data from core data. Lets say I am building a todo list app and I have a todo entity in core data. This entity has the attributes name and finishDate (the date on which the todo was marked as finished).

To plot these two variables, I need a variable containing each individual day and a variable containing the number of finished tasks on each specific day.

Does anyone know how I would go about creating this data efficiently? I know I can fetch the todo entity data and select the correct attributes. But how do I get the number of finished tasks on each specific day? Ideally without creating those variables using for loops.

It would be really appreciated if anyone can help me.

CodePudding user response:

This is easy using CoreData SwiftUI.

The code below is for iOS15 but you can do the same thing with an NSFetchedResultsController or NSFetchRequest / @FetchRequest and then group it. But it will require a bit of effort to stay real time.

Also, the code below is meant to work with the standard Apple code for a CoreData project. The only thing I changed in PersistenceController is setting a random day for the timestamp

newItem.timestamp = Date().addingTimeInterval(60*60*24*Double(Int.random(in: -10...10)))

This is a simple graph

import SwiftUI

@available(iOS 15.0, *)
struct DayChart: View {
    @SectionedFetchRequest(entity: Item.entity(), sectionIdentifier: \.completionDate, sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], predicate: nil, animation: Animation.linear)
    var sections: SectionedFetchResults<String, Item>
    @State var maxCount: Int = 1
    let spacing: CGFloat = 3
    var body: some View {
        VStack{
            GeometryReader{ geo in
                HStack(alignment: .bottom, spacing: spacing){
                    ForEach(sections){section in
                        VStack{
                            Text(section.count.description)
                            Rectangle()
                                .foregroundColor(.blue)
                                .onAppear(){
                                    maxCount = max(maxCount,section.count)
                                    
                                }
                            Text(section.id.description).minimumScaleFactor(0.5)
                            .lineLimit(2)
                        }.frame(width: (geo.size.width/CGFloat(sections.count) - spacing),height: geo.size.height * CGFloat(CGFloat(section.count)/CGFloat(maxCount)))
                    }
                }
            }
        }.padding(.leading, spacing)
    }
}

@available(iOS 15.0, *)
struct DayChart_Previews: PreviewProvider {
    static var previews: some View {
        DayChart().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}
extension Item{
    //This is the variable that determines your section/column/completion date
    @objc
    var completionDate: String{
                    
        if self.timestamp != nil{
            let dateFormatter: DateFormatter = DateFormatter()
            dateFormatter.dateFormat = "dd\nMMM"
            return dateFormatter.string(from: self.timestamp!)
             
        }else{
            return ""
        }
    }
}

enter image description here

CodePudding user response:

I’d start with an attribute for the day of the week (which you can derive from the Date() value you’re using for completion date). Then you can use either a count fetch request (Cocoa Core Data efficient way to count entities), or a group-by and an NSDictionaryResultType (extensive code example at https://www.cocoanetics.com/2017/04/group-by-count-and-sum-in-coredata/). The group-by approach will give you a dictionary that looks like this (from the Cocoanetics article):

[
   {
      "count": 111,
      "col": "One"
   },
   {
      "count": 222,
      "col": "Two"
   }
]

I believe, but don’t know for certain, that you’ll have to compute/store the day-of-week value as an attribute for these grouping approaches to work.

  • Related