Swift Extensions
Adding functionality to existing types
🔧 What are Swift Extensions?
Extensions add new functionality to existing classes, structures, enumerations, or protocols. You can extend types you don't own, including built-in Swift types, making your code more expressive and organized without modifying original implementations.
// Basic extension example
extension String {
func isPalindrome() -> Bool {
let cleaned = self.lowercased().replacingOccurrences(of: " ", with: "")
return cleaned == String(cleaned.reversed())
}
}
let text = "A man a plan a canal Panama"
print(text.isPalindrome()) // Output: true
let word = "hello"
print(word.isPalindrome()) // Output: false
Key Extension Features
Add Methods
Add new instance and type methods
extension Int {
func squared() -> Int {
return self * self
}
}
Computed Properties
Add computed properties to types
extension Double {
var km: Double { return self * 1000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
}
Initializers
Add convenience initializers
extension CGRect {
init(center: CGPoint, size: CGSize) {
let origin = CGPoint(x: center.x - size.width/2,
y: center.y - size.height/2)
self.init(origin: origin, size: size)
}
}
Protocol Conformance
Make types conform to protocols
extension String: Identifiable {
public var id: String { return self }
}
🔹 Extending Built-in Types
Add useful functionality to Swift's built-in types:
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0..<Swift.min($0 + size, count)])
}
}
var isNotEmpty: Bool {
return !isEmpty
}
}
extension Int {
var isEven: Bool {
return self % 2 == 0
}
var isOdd: Bool {
return !isEven
}
func times(_ closure: () -> Void) {
for _ in 0..<self {
closure()
}
}
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let chunks = numbers.chunked(into: 3)
print("Chunks: \(chunks)")
print("Is array not empty? \(numbers.isNotEmpty)")
let number = 4
print("\(number) is even: \(number.isEven)")
3.times {
print("Hello!")
}
Output:
Chunks: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Is array not empty? true
4 is even: true
Hello!
Hello!
Hello!
🔹 Extending Custom Types
Add functionality to your own types using extensions:
struct Person {
let firstName: String
let lastName: String
let age: Int
}
extension Person {
var fullName: String {
return "\(firstName) \(lastName)"
}
var initials: String {
return "\(firstName.first!)\(lastName.first!)"
}
func isAdult() -> Bool {
return age >= 18
}
func greet() -> String {
return "Hello, I'm \(fullName) and I'm \(age) years old."
}
static func createChild(firstName: String, lastName: String) -> Person {
return Person(firstName: firstName, lastName: lastName, age: 0)
}
}
let person = Person(firstName: "John", lastName: "Doe", age: 25)
print(person.fullName)
print("Initials: \(person.initials)")
print("Is adult: \(person.isAdult())")
print(person.greet())
let child = Person.createChild(firstName: "Jane", lastName: "Smith")
print("Child: \(child.fullName), Age: \(child.age)")
Output:
John Doe
Initials: JD
Is adult: true
Hello, I'm John Doe and I'm 25 years old.
Child: Jane Smith, Age: 0
🔹 Protocol Extensions
Provide default implementations for protocol methods:
protocol Describable {
var description: String { get }
}
extension Describable {
func printDescription() {
print("Description: \(description)")
}
func detailedDescription() -> String {
return "Detailed: \(description)"
}
}
struct Book: Describable {
let title: String
let author: String
var description: String {
return "\(title) by \(author)"
}
}
struct Movie: Describable {
let title: String
let director: String
let year: Int
var description: String {
return "\(title) (\(year)) directed by \(director)"
}
}
let book = Book(title: "1984", author: "George Orwell")
let movie = Movie(title: "Inception", director: "Christopher Nolan", year: 2010)
book.printDescription()
movie.printDescription()
print(book.detailedDescription())
print(movie.detailedDescription())
Output:
Description: 1984 by George Orwell
Description: Inception (2010) directed by Christopher Nolan
Detailed: 1984 by George Orwell
Detailed: Inception (2010) directed by Christopher Nolan
🔹 Conditional Extensions
Add functionality only when certain conditions are met:
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
func average() -> Double {
guard !isEmpty else { return 0 }
let total = sum()
return Double(total as! Double) / Double(count)
}
}
extension Array where Element == String {
func joinedWithCommas() -> String {
return joined(separator: ", ")
}
func longestString() -> String? {
return self.max { $0.count < $1.count }
}
}
let numbers = [1, 2, 3, 4, 5]
let doubles = [1.5, 2.5, 3.5, 4.5]
let names = ["Alice", "Bob", "Charlie", "David"]
print("Sum of numbers: \(numbers.sum())")
print("Average of doubles: \(doubles.average())")
print("Names: \(names.joinedWithCommas())")
print("Longest name: \(names.longestString() ?? "None")")
Output:
Sum of numbers: 15
Average of doubles: 3.0
Names: Alice, Bob, Charlie, David
Longest name: Charlie
🔹 Extension Best Practices
Follow these guidelines when creating extensions:
✅ Good Practices:
- Organize by functionality: Group related methods in the same extension
- Use meaningful names: Make extension methods clear and descriptive
- Separate concerns: Use different extensions for different protocols or features
- Document public extensions: Add comments for extensions others will use
- Consider performance: Extensions should not significantly impact performance
⚠️ Limitations:
- Cannot add stored properties (only computed properties)
- Cannot override existing methods
- Cannot add designated initializers to classes
- Cannot make a class inherit from another class