Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
75: Calendar and DateComponents
When you first start working with dates in Swift, it's incredibly tempting to treat time as a simple math problem. I've seen it a hundred times: a developer needs to find the date for "tomorrow" or "next month," and they reach for addingTimeInterval(). It feels intuitive because TimeInterval is just a Double representing seconds. You think, "Okay, a day has 86,400 seconds," and you multiply your way to the answer.
The danger of the magic number
Let's say we're building a trial period for a subscription service that lasts exactly one month. The naive approach looks something like this:
let trialEndDate = Date().addingTimeInterval(60 * 60 * 24 * 30)
On the surface, this works. But in production, this is a bug waiting to happen. Why? Because months aren't 30 days long. If your user signs up on January 30th, adding 30 days lands them in early March. If they sign up in February, they get an extra-long trial. More importantly, this approach completely ignores Daylight Savings Time (DST). When the clocks shift, a "day" isn't always 86,400 seconds; it might be 82,800 or 90,000. If you're scheduling a daily alarm for 8:00 AM using time intervals, your user is eventually going to wake up at 7:00 AM or 9:00 AM, and they're going to be very annoyed with you.
Letting the Calendar do the heavy lifting
The professional way to handle this is to stop thinking in seconds and start thinking in components. Swift provides the Calendar class to handle the messy reality of human time—leap years, varying month lengths, and timezone shifts. Instead of doing the math yourself, you tell the calendar what unit of time you want to change.
let calendar = Calendar.current
if let nextMonth = calendar.date(byAdding: .month, value: 1, to: Date()) {
print("Your trial ends on \(nextMonth)")
}
By using date(byAdding:value:to:), you're delegating the logic to the OS. If it's January 31st and you add a month, the system knows how to handle the transition to February correctly. It calculates the "calendar date" first, then converts that back into a Date object for you. It's a tiny bit more verbose because the method returns an optional, but the reliability gain is massive.
Precision control with DateComponents
Sometimes you need more granularity than just adding a single unit. Maybe you need to calculate a date that is exactly 2 years, 3 months, and 5 days from now. This is where DateComponents comes in. Think of DateComponents as a "description" of a date or a duration, rather than the date itself. It's like a recipe: you list the ingredients (year, month, day, hour), and the Calendar bakes it into a real Date.
var components = DateComponents()
components.year = 2
components.month = 3
components.day = 5
let futureDate = calendar.date(byAdding: components, to: Date())
I generally prefer this approach whenever I'm dealing with business logic. It makes the code self-documenting. When a colleague reads components.month = 3, they know exactly what the intent is. When they see addingTimeInterval(7776000), they have to pull out a calculator to figure out if you meant 90 days or just typed a random number. Use Date for timestamps and Calendar/DateComponents for any logic involving human-readable time.
📋 Practical Task
Exercise: Subscription Renewal Calculator
You are tasked with building the logic for a flexible subscription system. Create a function called calculateRenewalDate(from startDate: Date, monthsToAdd: Int, daysToAdd: Int) that returns an optional Date.
- The function should use
Calendar.current. - It must use
DateComponentsto combine both the months and days provided in the arguments. - The resulting date should be calculated relative to the
startDate.
Test your function by passing in a date from the end of a month (e.g., January 30th) and adding 1 month and 2 days to ensure the Calendar logic handles the month rollover correctly.
There are no comments for now.