Dynamically changing cell.update Date function? - ios

I am only new to all this so forgive my ignorance! I have a Table View to which I call a cell update method every time I open the Scene (it's sorted by date). I am trying to dynamically show a different date format depending on the when the Object was last edited. Example: Show time if date is today, Show "Yesterday" if date was yesterday etc. My question is - What is the best way to write this and is calling the below func in my cell.update call going to be memory intensive? How can I write this better? Any help would be greatly appreciated!
func update(with fitnessInfo: Fitness) {
if let date = fitnessInfo.dateEdited as Date? {
let today = Calendar.current.isDateInToday(date)
let yesterdayFunc = Calendar.current.isDateInYesterday(date)
let yesterday = date.addingTimeInterval(172800)
let withinSevenDays = date.addingTimeInterval(604800)
let dateFormatter = DateFormatter()
func setdate(_ dateFormatter: DateFormatter) {
let convertedDate = dateFormatter.string(from: date)
fitnessDateLabel.text = convertedDate
}
if today == true {
dateFormatter.dateFormat = "h:mm a"
setdate(dateFormatter)
} else if yesterdayFunc == true {
fitnessDateLabel.text = "Yesterday"
} else {
switch date {
case yesterday...withinSevenDays:
dateFormatter.dateFormat = "EEEE"
setdate(dateFormatter)
default:
dateFormatter.dateFormat = "MMMM dd, yyyy"
setdate(dateFormatter)
}
}
}
Here is the Table View func (cellForRowAt) which this is called from.
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: “fitnessCell", for: indexPath) as! FitnessTableViewCell
let fitnessInfo = fetchedRC.object(at: indexPath)
cell.update(with: fitnessInfo)
return cell
}

If code works it can not be bad.
It's a good idea to write unit- and performance-tests before any performance optimization. So you will see that your changes don't affect result and give performance improvement. Also you can use Xcode instruments to understand how exactly your code use CPU and memory by click Product->Profile and select Time profile and Allocations instruments. Don't forget that performance optimization is often a tradeoff between memory and CPU usage.
You should separate logic from presentation. So you be able to write unit-test. Also you can retain DateFrommater statically instead of initialize it each time you draw a cell. Here is an example how it can be done:
final class DateMapper {
static let todayDateFormatter = DateFormatter()
static let lastWeekAgoDateFormatter = DateFormatter()
static let moreThanWeekAgoDateFormatter = DateFormatter()
let today: Date
let currentCalendar = Calendar.current
init(today: Date) {
self.today = today
DateMapper.todayDateFormatter.dateFormat = "h:mm a"
DateMapper.lastWeekAgoDateFormatter.dateFormat = "EEEE"
DateMapper.moreThanWeekAgoDateFormatter.dateFormat = "MMMM dd, yyyy"
}
func formattedDate(date: Date) -> String {
guard let totalDays = currentCalendar.dateComponents([.day], from: date, to: today).day else {
return ""
}
switch totalDays {
case 0:
return DateMapper.todayDateFormatter.string(from: date)
case 1:
return "Yesterday"
case 2...7:
return DateMapper.lastWeekAgoDateFormatter.string(from: date)
default:
return DateMapper.moreThanWeekAgoDateFormatter.string(from: date)
}
}
}
This class can be used at your update method like this:
func update(with fitnessInfo: Fitness) {
let dateMapper = DateMapper(today: Date())
fitnessDateLabel.text = dateMapper.formattedDate(date: fitnessInfo.dateEdited)
}
Or you can retain DateMapper at ViewController and use it directly at cellForRowAt.
This class can be covered with unit test:
class ExampleTests: XCTestCase {
var dateMapper: DateMapper {
let todayStub = dateStubByComponents(year: 2019, month: 5, day: 4, hour: 12)
return DateMapper(today: todayStub)
}
func testTodayDate() {
// arrange
let todayStub = dateStubByComponents(year: 2019, month: 5, day: 4, hour: 11)
// act
let result = dateMapper.formattedDate(date: todayStub)
// assert
XCTAssertEqual(result, "11:00 AM")
}
func testYestadayDate() {
// arrange
let yesterdayStub = dateStubByComponents(year: 2019, month: 5, day: 3, hour: 0)
// act
let result = dateMapper.formattedDate(date: yesterdayStub)
// assert
XCTAssertEqual(result, "Yesterday")
}
func testLastWeekDate() {
// arrange
let lastWeekStub = dateStubByComponents(year: 2019, month: 5, day: 2, hour: 0)
// act
let result = dateMapper.formattedDate(date: lastWeekStub)
// assert
XCTAssertEqual(result, "Thursday")
}
func testPerformanceExample() {
let lastWeekStub = dateStubByComponents(year: 2019, month: 5, day: 2, hour: 0)
self.measure {
_ = dateMapper.formattedDate(date: lastWeekStub)
}
}
// MARK: Private
private func dateStubByComponents(year: Int, month: Int, day: Int, hour: Int) -> Date {
var dateStubComponents = DateComponents()
dateStubComponents.year = year
dateStubComponents.month = month
dateStubComponents.day = day
dateStubComponents.hour = hour
guard let dateStub = Calendar.current.date(from: dateStubComponents) else { fatalError("Date must be valid") }
return dateStub
}
}
At console you will see how long will take to transform Date to String:
values: [0.000922, 0.000063, 0.000041, 0.000038, 0.000036, 0.000035,
0.000035, 0.000036, 0.000043, 0.000027]

Related

Disabling all other days in calendar view

I have a form in which the user will select days and then select a date from calendar view..
for example: the user will first select sun and mon .. then click on date button an so calendar view will be shown ..
I want the user just be able to select dates in days sun or mon .. i want to disable the other days and highlight them for example ..
what is the best way to do that?
i saw these two libraries:
https://cocoapods.org/pods/JTAppleCalendar
https://github.com/WenchaoD/FSCalendar
but didn't find anything that helps me do what i need using them..
what is the best way to do that?
for JTApplecalendar this is easy
func calendar(_ calendar: JTAppleCalendarView, shouldSelectDate date: Date, cell: JTAppleCell?, cellState: CellState) -> Bool {
return cellState.day == .monday || cellState.day == .sunday
}
Done.
You have to consider Sunday as 1, Monday as 2, Tuesday as 3 and so on Saturday 7.
Define below globally
fileprivate let gregorian: Calendar = Calendar(identifier: .gregorian)
fileprivate lazy var dateFormatter2: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
var arrDates = NSMutableArray()
Write below line in viewDidLoad.
arrDates = self.getUserSelectedDates([1, 2], calender: self.calendarVW)
Here 1, 2 means user selected Monday and Tuesday (so This array contains only those dates which are Sunday and Monday)
Below is Function that returns Dates array Based on Day value like 1,2 and so on till 7.
func getUserSelectedDates(_ arrWeekDay: [Int], calender calenderVW: FSCalendar?) -> NSMutableArray {
let arrUnAvailibilityDates = NSMutableArray()
let currentDate: Date? = calenderVW?.currentPage
//get calender
let gregorianCalendar = Calendar.init(identifier: .gregorian)
// Start out by getting just the year, month and day components of the current date.
var components: DateComponents? = nil
if let aDate = currentDate {
components = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aDate)
}
// Change the Day component to 1 (for the first day of the month), and zero out the time components.
components?.day = 1
components?.hour = 0
components?.minute = 0
components?.second = 0
//get first day of current month
var firstDateOfCurMonth: Date? = nil
if let aComponents = components {
firstDateOfCurMonth = gregorianCalendar.date(from: aComponents)
}
//create new component to get weekday of first date
var newcomponents: DateComponents? = nil
if let aMonth = firstDateOfCurMonth {
newcomponents = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aMonth)
}
let firstDateWeekDay: Int? = newcomponents?.weekday
//get last month date
let curMonth: Int? = newcomponents?.month
newcomponents?.month = (curMonth ?? 0) + 1
var templastDateOfCurMonth: Date? = nil
if let aNewcomponents = newcomponents {
templastDateOfCurMonth = gregorianCalendar.date(from: aNewcomponents)?.addingTimeInterval(-1)
}
// One second before the start of next month
var lastcomponents: DateComponents? = nil
if let aMonth = templastDateOfCurMonth {
lastcomponents = gregorianCalendar.dateComponents([.year, .month, .day, .weekday], from: aMonth)
}
lastcomponents?.hour = 0
lastcomponents?.minute = 0
lastcomponents?.second = 0
var lastDateOfCurMonth: Date? = nil
if let aLastcomponents = lastcomponents {
lastDateOfCurMonth = gregorianCalendar.date(from: aLastcomponents)
}
var dayDifference = DateComponents()
dayDifference.calendar = gregorianCalendar
if arrWeekDay.count == 0 {
} else if arrWeekDay.count == 1 {
let wantedWeekDay = Int(arrWeekDay[0])
var firstWeekDateOfCurMonth: Date? = nil
if wantedWeekDay == firstDateWeekDay {
firstWeekDateOfCurMonth = firstDateOfCurMonth
} else {
var day: Int = wantedWeekDay - firstDateWeekDay!
if day < 0 {
day += 7
}
day += 1
components?.day = day
firstWeekDateOfCurMonth = gregorianCalendar.date(from: components!)
}
var weekOffset: Int = 0
var nextDate: Date? = firstWeekDateOfCurMonth
repeat {
let strDT: String = getSmallFormatedDate(convertCalendarDate(toNormalDate: nextDate))!
arrUnAvailibilityDates.add(strDT)
weekOffset += 1
dayDifference.weekOfYear = weekOffset
var date: Date? = nil
if let aMonth = firstWeekDateOfCurMonth {
date = gregorianCalendar.date(byAdding: dayDifference, to: aMonth)
}
nextDate = date
} while nextDate?.compare(lastDateOfCurMonth!) == .orderedAscending || nextDate?.compare(lastDateOfCurMonth!) == .orderedSame
}
else {
for i in 0..<arrWeekDay.count {
let wantedWeekDay = Int(arrWeekDay[i])
var firstWeekDateOfCurMonth: Date? = nil
if wantedWeekDay == firstDateWeekDay {
firstWeekDateOfCurMonth = firstDateOfCurMonth
} else {
var day: Int = wantedWeekDay - firstDateWeekDay!
if day < 0 {
day += 7
}
day += 1
components?.day = day
firstWeekDateOfCurMonth = gregorianCalendar.date(from: components!)
}
var weekOffset: Int = 0
var nextDate: Date? = firstWeekDateOfCurMonth
repeat {
let strDT = getSmallFormatedDate(convertCalendarDate(toNormalDate: nextDate))
arrUnAvailibilityDates.add(strDT!)
weekOffset += 1
dayDifference.weekOfYear = weekOffset
var date: Date? = nil
if let aMonth = firstWeekDateOfCurMonth {
date = gregorianCalendar.date(byAdding: dayDifference, to: aMonth)
}
nextDate = date
} while nextDate?.compare(lastDateOfCurMonth!) == .orderedAscending || nextDate?.compare(lastDateOfCurMonth!) == .orderedSame
}
}
return arrUnAvailibilityDates
}
func getSmallFormatedDate(_ localDate: Date?) -> String? {
let dateFormatter = DateFormatter()
let timeZone = NSTimeZone(name: "UTC")
if let aZone = timeZone {
dateFormatter.timeZone = aZone as TimeZone
}
dateFormatter.dateFormat = "yyyy-MM-dd"
var dateString: String? = nil
if let aDate = localDate {
dateString = dateFormatter.string(from: aDate)
}
return dateString
}
func convertCalendarDate(toNormalDate selectedDate: Date?) -> Date? {
let sourceTimeZone = NSTimeZone(abbreviation: "UTC")
let destinationTimeZone = NSTimeZone.system as NSTimeZone
var sourceGMTOffset: Int? = nil
if let aDate = selectedDate {
sourceGMTOffset = sourceTimeZone?.secondsFromGMT(for: aDate)
}
var destinationGMTOffset: Int? = nil
if let aDate = selectedDate {
destinationGMTOffset = destinationTimeZone.secondsFromGMT(for: aDate)
}
let interval1 = TimeInterval((destinationGMTOffset ?? 0) - (sourceGMTOffset ?? 0))
var localDate: Date? = nil
if let aDate = selectedDate {
localDate = Date(timeInterval: interval1, since: aDate)
}
return localDate
}
Below is FSCalendar delegates
extension ViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance {
func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) {
self.view.layoutIfNeeded()
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
arrDates = self.getUserSelectedDates([3, 4], calender: self.calendarVW)
}
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? {
if arrDates.contains(dateFormatter2.string(from: date)) {
return UIColor.green
} else {
return UIColor.red
}
}
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
if arrDates.contains(dateFormatter2.string(from: date)) {
return true
}
else {
return false
}
}
}
For function calendar check Saturday or Sunday then it did not select.
The solution for Swift 4.2:
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
return CheckSatSunday(today: date)
}
// Check Today Is Saturday or Sunday
func CheckSatSunday(today:Date) ->Bool{
var DayExist:Bool
// let today = NSDate()
let calendar =
NSCalendar(calendarIdentifier:NSCalendar.Identifier.gregorian)
let components = calendar!.components([.weekday], from: today)
if components.weekday == 1 {
print("Hello Sunday")
self.showToast(message: "Sunday is Off")
DayExist = false
} else if components.weekday == 7{
print("Hello Saturday")
self.showToast(message: "Saturday is Off")
DayExist = false
} else{
print("It's not Saturday and Sunday ")
DayExist = true
}
print("weekday :\(String(describing: components.weekday)) ")
return DayExist
}

How to get a half of every month from current year in Swift

In my application i have an option to enter the data for every 15days.I have to maintain this for an current year.Please help me to figure out this problem.
For ex: [
"1-1-2018 to 15-1-2018", "16-1-2018 to 31-1-2018",
"1-2-2018 to 15-2-2018", "16-2-2018 to 28-2-2018",
"1-3-2018 to 15-3-2018", "16-3-2018 to 31-3-2018",
"1-4-2018 to 15-4-2018", "16-4-2018 to 30-4-2018",
"1-5-2018 to 15-5-2018", "16-5-2018 to 31-5-2018",
"1-6-2018 to 15-6-2018", "16-6-2018 to 30-6-2018",
"1-7-2018 to 15-7-2018", "16-7-2018 to 31-7-2018",
"1-8-2018 to 15-8-2018", "16-8-2018 to 31-8-2018",
"1-9-2018 to 15-9-2018", "16-9-2018 to 30-9-2018",
"1-10-2018 to 15-10-2018", "16-10-2018 to 31-10-2018",
"1-11-2018 to 15-11-2018", "16-11-2018 to 30-11-2018",
"1-12-2018 to 15-12-2018", "16-12-2018 to 31-12-2018"
]
From Calendar API you can get total number of days for any month in the given date and also first day of the month like below,
extension Calendar {
public func firstDayOfMonth(date: Date) -> Date {
let components = self.dateComponents([.year, .month], from: date)
return self.date(from: components) ?? date
}
public func numberOfDaysInMonthFor(date: Date) -> Int {
let range = self.range(of: .day, in: .month, for: date)
return range?.count ?? 0
}
public func lowerHalfOfMonthFor(date: Date) -> (Date, Date) {
let startDate = self.firstDayOfMonth(date: date)
let endDate = startDate.dateByAppending(day: 14)
return (startDate, endDate)
}
public func upperHalfOfMonthFor(date: Date) -> (Date, Date) {
let firstDayOfMonthDate = self.firstDayOfMonth(date: date)
let totalNoOfDaysInMonth = self.numberOfDaysInMonthFor(date: firstDayOfMonthDate)
let startDate = firstDayOfMonthDate.dateByAppending(day: 15)
let endDate = firstDayOfMonthDate.dateByAppending(day: totalNoOfDaysInMonth - 1)
return (startDate, endDate)
}
}
you can also extend Date to get new date by appending any number of days,
extension Date {
public func dateByAppending(day: Int) -> Date {
let newDate = Calendar.current.date(byAdding: .day, value: day, to: self)
return newDate ?? self
}
public func daysDifference(_ date: Date?) -> Int? {
guard let date = date else { return nil }
return Calendar.current.dateComponents([.day], from: self, to: date).day
}
With the mix of above helper methods, you should be able to achieve the required result like below,
let date = Date()
let lowerHalf = Calendar.current.lowerHalfOfMonthFor(date: date)
let uppperHalf = Calendar.current.upperHalfOfMonthFor(date: date)
Below code will calculate the 15th day if you give the input,
static func getFortnightly(selectedDate : String) -> String?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yy" //Your date format
if let dateSelected = dateFormatter.date(from: selectedDate) {//according to d
let newDate = Calendar.current.date(byAdding: .weekOfYear, value: 2, to: dateSelected)
let convertedDateToString = dateFormatter.string(from: newDate!)
return convertedDateToString
}
return nil
}
}

Displaying Calendar Dates in Swift

Im trying to build a weekly calendar for IOS.
The problem with that sample is code is: the usage of an dates array
let dates = ["7/10/2017", "7/11/2017", "7/12/2017", "7/13/2017", "7/14/2017", "7/15/2017", "7/16/2017"]
func spreadsheetView(_ spreadsheetView: SpreadsheetView, cellForItemAt indexPath: IndexPath) -> Cell? {
if case (1...(dates.count + 1), 0) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier: String(describing: DateCell.self), for: indexPath) as! DateCell
cell.label.text = dates[indexPath.column - 1]
return cell
Filling that array with real dates from 01.01.2000 - 31.12.2099 e.g. leads to really bad performance of the app.
Does anyone know how to display the current Dates in an more elegant way?
You could do so by using the following extension:
See here : Swift: Print all dates between two NSDate()
extension Date{
func generateDatesArrayBetweenTwoDates(startDate: Date , endDate:Date) ->[Date]
{
var datesArray: [Date] = [Date]()
var startDate = startDate
let calendar = Calendar.current
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
while startDate <= endDate {
datesArray.append(startDate)
startDate = calendar.date(byAdding: .day, value: 1, to: startDate)!
}
return datesArray
}
}
Usage:
let dates = Date().generateDatesArrayBetweenTwoDates(startDate: Your Start Date Object , endDate: Your End Date Object)
Here's one solution using Calendar enumerateDates:
// 01.01.2000
let startComps = DateComponents(year: 2000, month: 1, day: 1)
let startDate = Calendar.current.date(from: startComps)!
// 31.12.2099
let endComps = DateComponents(year: 2099, month: 12, day: 31)
let endDate = Calendar.current.date(from: endComps)!
let components = DateComponents(hour: 0, minute: 0, second: 0) // midnight
var dates = [startDate]
Calendar.current.enumerateDates(startingAfter: startDate, matching: components, matchingPolicy: .nextTime) { (date, strict, stop) in
if let date = date {
if date <= endDate {
dates.append(date)
} else {
stop = true
}
}
}

How to get week start date and end date by using any Month and week number swift 3?

I have to implement graph so that I need to get week start date and weekend date if I will pass the date object and week number.
How can I achieve that I tried it but didn't get exactly?
Here below is my code:-
Weekday:-
//Day of week
func getDayOfWeek(today:String)->Int? {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
if let todayDate = formatter.date(from: today) {
let myCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!
let myComponents = myCalendar.components(.weekday, from: todayDate)
let weekDay = myComponents.weekday
return weekDay
} else {
return nil
}
}.
extension Date {
var millisecondsSince1970:Int {
return Int((self.timeIntervalSince1970 * 1000.0).rounded())
}
init(milliseconds:Int) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
}
func startOfWeek(weekday: Int?) -> Date {
var cal = Calendar.current
var component = cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
component.to12am()
cal.firstWeekday = weekday ?? 1
return cal.date(from: component)!
}
func endOfWeek(weekday: Int) -> Date {
let cal = Calendar.current
var component = DateComponents()
component.weekOfYear = 1
component.day = -1
component.to12pm()
return cal.date(byAdding: component, to: startOfWeek(weekday: weekday))!
}
}
internal extension DateComponents {
mutating func to12am() {
self.hour = 0
self.minute = 0
self.second = 0
}
mutating func to12pm(){
self.hour = 23
self.minute = 59
self.second = 59
}
}
This returns start- and end date for a given week number and date
func dayRangeOf(weekOfYear: Int, for date: Date) -> Range<Date>
{
let calendar = Calendar.current
let year = calendar.component(.yearForWeekOfYear, from: date)
let startComponents = DateComponents(weekOfYear: weekOfYear, yearForWeekOfYear: year)
let startDate = calendar.date(from: startComponents)!
let endComponents = DateComponents(day:7, second: -1)
let endDate = calendar.date(byAdding: endComponents, to: startDate)!
return startDate..<endDate
}
print(dayRangeOf(weekOfYear: 12, for: Date()))
Consider that print displays the dates in UTC and the start date depends on the first weekday setting of the current locale.
Edit
A version to determine the range of a given week of month
func dayRangeOf(weekOfMonth: Int, year: Int, month: Int) -> Range<Date>? {
let calendar = Calendar.current
guard let startOfMonth = calendar.date(from: DateComponents(year:year, month:month)) else { return nil }
var startDate = Date()
if weekOfMonth == 1 {
var interval = TimeInterval()
guard calendar.dateInterval(of: .weekOfMonth, start: &startDate, interval: &interval, for: startOfMonth) else { return nil }
} else {
let nextComponents = DateComponents(year: year, month: month, weekOfMonth: weekOfMonth)
guard let weekStartDate = calendar.nextDate(after: startOfMonth, matching: nextComponents, matchingPolicy: .nextTime) else {
return nil
}
startDate = weekStartDate
}
let endComponents = DateComponents(day:7, second: -1)
let endDate = calendar.date(byAdding: endComponents, to: startDate)!
return startDate..<endDate
}
print(dayRangeOf(weekOfMonth: 5, year: 2017, month: 6))
The result type of the second version is an optional because there are a few calculations which could fail for example if the number of week in the particular month is out of range.
For anyone interested in this, it looks like OP confusing weekOfMonth and weekOfYear…
//: Playground - noun: a place where people can play
import UIKit
var str = "Hello, playground"
let cal = Calendar.current
let dateComponents = DateComponents(year: 2018, month: 3, day: 15)
let date = cal.date(from: dateComponents)!
func weekOfMonthStart(forDate date: Date) -> Date {
var compsToWeekOfMonth = cal.dateComponents([.year, .month, .weekOfYear], from: date)
compsToWeekOfMonth.day = cal.range(of: .day, in: .weekOfMonth, for: date)?.lowerBound
return cal.date(from: compsToWeekOfMonth)!
}
Somebody mention an answer that will fail, so a test was included ;)
for i in 0...5000 {
let newDate = cal.date(byAdding: DateComponents(day:i), to: date)!
weekOfMonthStart(forDate: newDate)
}

Results of sorting an array are unexpected

I have a an array of objects that contains a date value. I have calculated the date differences and returned the number of days left. Now I am trying to sort it so it appends based on the object with least number of days left.
I have been able to use this function:
func sortList() {
item.sort { (first: Item, second: Item) -> Bool in
return first.days() < second.days()
}
}
Which gives me this:
However as you can see the date which is equal to 0 is appended at the bottom.
This is how I am calculating the days difference:
func daysDiff(startDate: Date, endDate: Date) -> Int {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: startDate)
let date2 = calendar.startOfDay(for: endDate)
let a = calendar.dateComponents([.day], from: date1, to: date2)
return a.value(for: .day)!
}
And this is how I am formatting it:
func days() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}
I am not really sure why this issue is happening.
Your sort is based on the text from your days() function so you are sorting the data alphabetically based on your text. You should sort based on an actual integer value, not a string.
You should have two methods on your class.
days() which returns an Int instead of a String.
daysLabel which returns a String based on the result of days.
Use days when sorting by number. Use daysLabel when displaying an Item instance somewhere.
func days() -> Int {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
return days
}
func daysLabel() -> String {
let days = days()
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}

Resources