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
65: Sorting and Custom Comparators
Swift's built-in sorting is a breeze when you're dealing with Ints or Strings, but as soon as you start working with your own custom models, the compiler is going to stop you in your tracks. It doesn't know if a "User" should be sorted by their join date, their username, or their account balance. That's where custom comparators come in.
I've found that the most practical way to master this is to build a priority queue of sorts. Let's imagine we're building a simple task manager. We don't just want to sort by the name of the task; we want to sort by priority level, and if two tasks have the same priority, we want the one due sooner to come first.
Defining the Task model
First, we need a data structure. I'll keep this lean. We need a title, a priority level (where a higher number means more urgent), and a deadline.
struct Task {
let title: String
let priority: Int
let dueDate: Date
}
let myTasks = [
Task(title: "Fix critical login bug", priority: 3, dueDate: Date().addingTimeInterval(86400)),
Task(title: "Update documentation", priority: 1, dueDate: Date().addingTimeInterval(172800)),
Task(title: "Email client", priority: 2, dueDate: Date().addingTimeInterval(43200)),
Task(title: "Refactor API layer", priority: 3, dueDate: Date().addingTimeInterval(20000))
]
The basic sorted closure
If I just call myTasks.sorted(), Swift will throw a compiler error because Task doesn't conform to Comparable. I could make it conform, but often you want to sort the same list in different ways depending on the screen the user is looking at. The sorted(by:) method is much more flexible here because it takes a closure that returns a Boolean.
Let's start by sorting just by priority. I want the highest priority at the top.
let prioritySorted = myTasks.sorted { $0.priority > $1.priority }
The $0 and $1 are just shorthand for the two elements Swift is currently comparing. If the closure returns true, the first element comes before the second.
Correcting a logic flip
Here is where I usually trip up when I'm rushing. I'll write a sort for the dates, thinking "earlier is better," and I'll end up with a list that's completely backwards.
// My first attempt at sorting by date
let dateSorted = myTasks.sorted { $0.dueDate > $1.dueDate }
Wait. I just realized my list is showing the tasks due furthest in the future first. Because I used the "greater than" operator, I'm telling Swift that a date further in the future is "less than" (should come before) an earlier date in terms of sorting order. That's not what I want for a deadline list.
I need to flip that operator to < so that the earliest date (the smallest value) takes precedence:
let dateSortedCorrected = myTasks.sorted { $0.dueDate < $1.dueDate }
Building a multi-level comparator
Now for the real-world scenario. A single-property sort is rarely enough. We want our tasks sorted by priority first, but we need a "tie-breaker" for when priorities are equal. In this case, the tie-breaker is the due date.
To do this, we move away from the shorthand $0 syntax and write a more explicit closure. This makes the logic much easier to read and maintain.
let finalSortedTasks = myTasks.sorted { (task1, task2) -> Bool in
if task1.priority != task2.priority {
// Different priorities? Sort by the highest priority first.
return task1.priority > task2.priority
} else {
// Same priority? Use the date as the tie-breaker (earliest first).
return task1.dueDate < task2.dueDate
}
}
Now, the "Refactor API layer" task (Priority 3, sooner date) will correctly sit above the "Fix critical login bug" task (Priority 3, later date), and both will sit above the lower priority tasks. This pattern—checking for inequality and then falling back to a second property—is the bread and butter of professional data sorting in Swift.
📋 Practical Task
Building a Game Leaderboard with Tie-Breaking Logic
You are building a leaderboard for a mobile game. You have a Player struct with two properties: score: Int and timeTaken: Double (representing the seconds it took to complete the level).
Your Goal: Create a sorted list of players based on these rules:
- Players with a higher score must appear first.
- If two players have the exact same score, the player who completed the level in less time must appear first.
Implement the Player struct and a sorting mechanism for the following data set:
let players = [
Player(name: "Alice", score: 100, timeTaken: 45.2),
Player(name: "Bob", score: 150, timeTaken: 60.1),
Player(name: "Charlie", score: 150, timeTaken: 55.5),
Player(name: "Diana", score: 80, timeTaken: 30.0)
]
Print the final sorted list to the console to verify that Bob and Charlie are at the top, with Charlie appearing before Bob due to his faster time.
There are no comments for now.