I have several HistoryItems which contain an ID, Title and Date.
The Date is an NSDate of the exact time it was added to the history down to the second.
At the moment I just have an NSArray of these objects, the latest HistoryItem added to the end of the NSArray.
How do I make these items display in a UItableView cell in order of date, the latest being in the top, but also each day must have it's own section.
I also need these HistoryItems to be persistent between exit and loading of apps, so should I go with just saving the NSArray to a file, or using CoreData?
Using CoreData will probably be easier since you can use NSFetchedResultsController to manage the sections 'automatically'. Actually using a NSDate as a sectionKeyPath can be a bit problematic but as luck has it Apple has some sample code that you should be able to modify from having a section per month to a section per day.
Going with CoreData also gives you the sorting essentially for free.
Related
In Core Data I have an Entity with a date field and I want to display all items on UI for a specific date range. For example, I want to show a list of items for the current month with the ability to switch the month.
I found that the best way to do this would be using NSFetchedResultsController with using sectionNameKeyPath which would be computed based on the date field like this:
extension MyEntity {
var sectionId: String {
// convert date to year+month_number
}
}
With this solution, I would get separate sections of items for each month and also not have empty pages where there are no items for that date range.
The problem is when there are many items in the storage - it takes too much time to fetch them cause NSFetchedResultsController fetches all of them at once by default. I tried to use fetchLimit, but that does not work for me, because it limits the number of fetched objects, but in my case, I would need to limit the number of fetched sections.
I was also thinking about just having a predicate that would limit the date range like this
date > startOfMonth & date < endOfMonth
But then I don't know if there are any items for that date range and I don't want to show an empty list.
Is there any way I can fetch a limited number of sections? Or maybe there is a better way to do this?
Can some body tell me the best way to create the Day entity in Coredata in one go for a diary app?
PS : I know how to make a notes app in which i can insert notes in DB, but if the don't insert notes in a specific day then the day is not shown when fetched.
I can't seem to understand how to put the notes in the respectetive days in core data ?
You could insert all the dates at the start by choosing a start date, an end date, and then using NSCalendar methods to go from one to the other one day at a time. Add a new entry for each date, and you're done.
That would be a really bad design though. It doesn't make sense to create new entries in your persistent store for every possible day you might cover. Better to only store data that you actually need than to waste time and space for data you'll never use.
To show every day in a week or month or whatever, show those dates, don't rely on Core Data to have every possible date. Show every day in the range that your UI covers. Fetch every diary entry for those dates. If there are diary entries on the date, show them. If there are no diary entries for a date, show the date with no entries. Showing every day in a specific range is a function of your controller code. It should choose the dates and ask Core Data what it has for those dates.
Ok, so you should subclass your entities first so they are easier to work with. And then you could add a function that returns an array of the object (entity) that is your diary. e.g
func getData(moc: NSManagedObjectContext) -> [Entity] {
let request: NSFetchRequest<Entity> = Entity.fetchRequest()
do {
let entityData = try moc.fetch(request)
return entityData
} catch {
// Handle errors
}
}
You could then add a predicate to this method that returns specific data inside the entity matching the arguments you pass your predicate. I know this doesn't cover your full question but i hope it helps!
I've been looking to query for all of the "posts" a user has made, which I've done, and then separate them into sections in a UICollectionViewCell based on their createdAt day. So that my collection view headers will have the day and the under there will be to posts made that day. Perhaps I'm overthinking this too much? Thanks for any help!
This answer provides a way to get the start and end dates for the current day.
You'll then have to figure out how to sort the objects, but I'll leave that up to you.
Separating a lot of objects out into sections with NSDate comparisions may or may not affect your app's performance, so you'll have to be wary of that, but don't worry about it if you don't notice anything.
Background info: I have a simple tally counter/habit-tracking app that populates a tableview with custom cells - the counters. Tapping on a cell brings up a detailed view of a specific counter that has the name, value, and the time period for that counter (daily/weekly/monthly/yearly/total). I have also stored in CoreData the startDate and endDate for each counter, so each counter resets after a certain time.
What I would like to do: Each time a counter resets (e.g. after a day or a week), I would like its current total value to be added to a log (preferably some sort of array) specific to that counter. This will then populate another tableview so that, after a few weeks, I can look back and see the previous weekly totals and compare it to the current week.
My data structure:
Entity: Counter
Attributes: Value (Int), startDate(NSDate), endDate(NSDate), timePeriod(Int) (Note: For timePeriod, each integer from 0 to 4 represents daily/weekly/monthly/yearly/no reset.)
My question: How can I implement this? Do I create another entity with a date and value attribute that is created each time a counter resets? I'm having trouble visualizing how to do this with CoreData.
P.S. I don't think using tableview and fetch requests is what I'm looking for.
Thanks so much for your help, and ask me if you need any clarification!
The simplest way is to not "reset" the counter but create a new one. You can then easily display the past counters. That would avoid creating a new entity.
To make it even simpler to distinguish them from active counters you could add a flag like active or archived. You could use that flag for a convenient predicate in your fetched results controller.
I am building a calendar for iOS based on UICollectionView (GitHub link), based on the one that you can find at this repo. The underlying reasoning behind it is well described on this Objc.io issue.
To put it short, the process involves creating a NSFetchedResultsController that queries the events grouping them by day and displaying them in a (customized) UICollectionView afterwards.
The original calendar has the following inconvenient: since it is based on the sections contained in NSFetchedResultsController, it simply puts the date as the section header but ignores the days where there are no events happening. In this scenario, the index of the section contained in the NSFetchedResultsController matches 1:1 the sections of the calendar.
I made modifications in order to base the calculation of section headers based on calendar days, but now there is not a 1:1 correspondence anymore between the sections in the NSFetchedResultsController and the sections of the UICollectionView. Remember that the sections are based on the days.
This is the way I am currently doing it now, when I need to get the index of the section for a certain day:
- (NSInteger)sectionForDate:(NSDate*)day
{
return [[self.fetchedResultsController.sections valueForKey:#"name"] indexOfObject:[NSString stringWithFormat:#"%#", day]];
}
Here's the question: is there a more efficient way to get the section index out of the NSFetchedResultsController ?
If you think I should have used a different approach, please let me know.
There is absolutely no problem in having different sections from the FRC and in your datasource methods. The logic becomes a little bit more complicated, but it is a not so unusual pattern. There should be no drawbacks in efficiency.
I recently wrote an app where you can even switch between showing all days or only those with events, similar to Apple's calendar where in the list view the "empty" days are skipped as well.