SwiftUI Week Calendar View. #2: Models


Greetings, traveler!

We continue our series about SwiftUI Week Calendar View. This is the second part. If you just want to check out the complete source code, here it is.

First, let’s create models for our task.

# Week Position

As we mentioned before, we will use a TabView. Even though we have an infinite Week Calendar, we will use only three tabs. We will update them in time to represent three generated weeks. So, there will be three positions for every week: left, middle, and right. Let’s create an appropriate model, then.

public enum WeekPosition {
    case left
    case middle
    case right
}

The difference between these weeks is simple. The first day of each week is a certain distance from the other two. We will start counting from the middle one. So, we can add a rawValue for our enum cases.

public enum WeekPosition: Int {
    case left = -7
    case middle = 0
    case right = 7
}

# Week

Now, we can create a Week model. This model has two variables. The first one will represent a date, from which we will start to calculate. The second one will hold all weekdays as an array of Dates.

public struct Week {

    public let dates: [Date]
    public let referenceDate: Date 
    
    public init(dates: [Date], referenceDate: Date) {
        self.dates = dates
        self.referenceDate = referenceDate
    }
    
}

To generate weeks for every position, we must be able to compose this array based on the WeekPosition’s rawValue. Since we have a reference date, we can calculate the same day at the desired position. After that, we can calculate the start of the week and add another six days to our array.

# Same day in another week and Week generation

We can use the Foundation’s Calendar to calculate the same day in another week. Since we will use it more than once, let’s create an extension for Date. It will be safer to get the date and calculate the start of the day. We can do it with Calendar, too.

extension Date {
    
    var startOfTheDay: Date {
        Calendar.current.startOfDay(for: self)
    }
    
    func sameDay(at position: WeekPosition) -> Date {
        (Calendar.current.date(byAdding: .day, value: position.rawValue, to: self) ?? .now).startOfTheDay
    }

}

We can use it in our function now. For convenience, we will place this function in our Week model.

public struct Week {

    public let dates: [Date]
    public let referenceDate: Date
    
    public init(dates: [Date], referenceDate: Date) {
        self.dates = dates
        self.referenceDate = referenceDate
    }
    
    public static func week(
        for date: Date,
        at position: WeekPosition
    ) -> Self {
        let date = date.sameDay(at: position)
        let startOfWeek = Calendar.current.date(from: Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))
        let weekDates = (0...6).compactMap {
            Calendar.current.date(
                byAdding: .day,
                value: $0,
                to: startOfWeek ?? .now
            )
        }
        
        return Week(
            dates: weekDates,
            referenceDate: date
        )
    }
    
}

Conclusion

Our models are almost ready. We will add some additional functionality when it is needed. Meanwhile, I invite you to the third part of our Week Calendar series.