Calendar's dateInterval and RecurrenceRule
While making a demonstration mode for Your WasteCalendar, I wanted to create a waste schedule that creates a pickup moment for every Thursday in the entire year. But how do you get 'every Thursday in 2026'?
Luckily, Apple Foundation's Calendar provides two very useful APIs to deal with this.
Calendar.dateInterval(of:for:)
With Calendar.dateInterval(of:for:), you can easily get the range of a date component of a range. For example, you can get the start and end date of the year of the current date.
let date = Date.now // 17th of July 2026
let interval = calendar.dateInterval(of: .year, for: date)!
interval.start // 1st of January 2026
interval.end // 1st of January 2027
Calendar.RecurrenceRule
You can use a Calendar.RecurrenceRule to search for events that match some criteria in the future. This can be useful for creating a weekly reminder until the end of the year, for example. Make a rule that triggers every Thursday and then ask the rule for its recurrences.
// We also use the `date` and `interval` of the previous code block.
let rule = Calendar.RecurrenceRule(
calendar: Calendar(identifier: .gregorian),
frequency: .weekly,
weekdays: [.every(.thursday)]
)
let thursdays = rule.recurrences(of: date, in: interval.start..<interval.end)
// The first Thursday would be 23rd of July, 2026.
// And the last would be on the 31st of December, 2026.
With their powers combined!
With those two methods, you can create a function like this;
public func every(_ weekdays: [Locale.Weekday], of component: Calendar.Component, in referenceDate: Date) -> [Date] {
guard let dateInterval = dateInterval(of: component, for: referenceDate) else {
return []
}
let recurrenceRule = Calendar.RecurrenceRule(
calendar: self,
frequency: .weekly,
weekdays: weekdays.map({ .every($0) })
)
let result = recurrenceRule.recurrences(of: dateInterval.start, in: dateInterval.start..<dateInterval.end)
return Array(result)
}
And that function then allows you quickly search for useful days;
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 2
let weekend = [Locale.Weekday.saturday, .sunday]
let date = Date.now // Friday, 17th of July 2026
calendar.every(weekend, of: .weekOfMonth, in: date)
// Returns the 18th and 19th of 2026.
What's you favorite Calendar tip? Let me know!