Swift Extensions

Main Idea

Extensions let us add functionality to any type, whether we created it or someone else created it – even one of Apple’s own types.

Extensions are also useful for organizing our own code, and although there are several ways of doing this I want to focus on two here: conformance grouping and purpose grouping.

Conformance grouping means adding a protocol conformance to a type as an extension, adding all the required methods inside that extension. This makes it easier to understand how much code a developer needs to keep in their head while reading an extension – if the current extension adds support for Printable, they won’t find printing methods mixed in with methods from other, unrelated protocols.

On the other hand, purpose grouping means creating extensions to do specific tasks, which makes it easier to work with large types. For example, you might have an extension specifically to handle loading and saving of that type.


var quote = "   The truth is rarely pure and never simple   "
let trimmed = quote.trimmingCharacters(in: .whitespacesAndNewlines)

extension String {
    func trimmed() -> String {
        self.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

let trimmed = quote.trimmed()

// Equalivilent to 
func trim(_ string: String) -> String {
    string.trimmingCharacters(in: .whitespacesAndNewlines)
}
let trimmed2 = trim(quote)

// However the extension has a number of benefits over the global function, including:
// 1. When you type quote. Xcode brings up a list of methods on the string, including all the ones we add in extensions. This makes our extra functionality easy to find.
// 2. Writing global functions makes your code rather messy – they are hard to organize and hard to keep track of. On the other hand, extensions are naturally grouped by the data type they are extending.
// 3. Because your extension methods are a full part of the original type, they get full access to the type’s internal data. That means they can use properties and methods marked with private access control, for example.

// What’s more, extensions make it easier to modify values in place – i.e., to change a value directly, rather than return a new value.

mutating func trim() {
    self = self.trimmed()
}
quote.trim()

// You can also use extensions to add properties to types, but there is one rule: they must only be computed properties, not stored properties. The reason for this is that adding new stored properties would affect the actual size of the data types – if we added a bunch of stored properties to an integer then every integer everywhere would need to take up more space in memory, which would cause all sorts of problems.
var lines: [String] {
    self.components(separatedBy: .newlines)
}

// TIPS keep memberwise initializer in struct and create custom initializer 

struct Book {
    let title: String
    let pageCount: Int
    let readingHours: Int
}

let lotr = Book(title: "Lord of the Rings", pageCount: 1178, readingHours: 24)

extension Book {
    init(title: String, pageCount: Int) {
        self.title = title
        self.pageCount = pageCount
        self.readingHours = pageCount / 50
    }
}

// Example 

let guests = ["Mario", "Luigi", "Peach"]

// This NO
if guests.isEmpty == false {
    print("Guest count: \(guests.count)")
}

// This NO
if !guests.isEmpty {
    print("Guest count: \(guests.count)")
}

// Try This
extension Array {
    var isNotEmpty: Bool {
        isEmpty == false
    }
}

if guests.isNotEmpty {
    print("Guest count: \(guests.count)")
}

// How about Set, Dict, and Array
extension Collection {
    var isNotEmpty: Bool {
        isEmpty == false
    }
}

// Extend this to the entire project protocol-oriented programming – we can list some required methods in a protocol, then add default implementations of those inside a protocol extension. 

// Example 1
protocol Person {
    var name: String { get }
    func sayHello()
}

extension Person {
    func sayHello() {
        print("Hi, I'm \(name)")
    }
}

struct Employee: Person {
    let name: String
}

let taylor = Employee(name: "Taylor Swift")
taylor.sayHello()

// Example 2

let numbers = [4, 8, 15, 16]
let allEven = numbers.allSatisfy { $0.isMultiple(of: 2) }


let numbers2 = Set([4, 8, 15, 16])
let allEven2 = numbers2.allSatisfy { $0.isMultiple(of: 2) }

let numbers3 = ["four": 4, "eight": 8, "fifteen": 15, "sixteen": 16]
let allEven3 = numbers3.allSatisfy { $0.value.isMultiple(of: 2) }

// Of course, the Swift developers don’t want to write this same code again and again, so they used a protocol extension: they wrote a single allSatisfy() method that works on a protocol called Sequence, which all arrays, sets, and dictionaries conform to. This meant the allSatisfy() method immediately became available on all those types, sharing exactly the same code.

Notes mentioning this note


Here are all the notes in this garden, along with their links, visualized as a graph.

100DaysofSwiftUIAlgorithmsAffirmation TimerBPM ClapperBPM TrainingMetronome Vintage 3DHackingWithSwiftSwiftUI Accessibility Hiding and Grouping DataSwiftUI Accessibility Identifying ViewsSwiftUI Accessibility Read Value ControlsSwiftUI Accessibility Support as NeededSwiftUI AccessibilitySwiftUI Advanced ViewsSwiftUI CGAffineTransformSwiftUI Drawing animatableDataSwiftUI Drawing Special EffectsSwiftUI DrawingSwiftUI ImagePaintSwiftUI MetalSwiftUI PathsSwiftUI ShapesSwiftUI Image AlbumImage GeometryReaderSwiftUI Image InterpolationSwiftUI ImageSwiftUI Intergrate UIKitSwiftUI Basic ViewsSwiftUI ButtonsSwiftUI ColorSwiftUI GradientSwiftUI DatePickerSwiftUI Form ValidationSwiftUI FormSwiftUI SliderSwiftUI StepperSwiftUI TextEditorSwiftUI GridSwiftUI GroupsSwiftUI ListSwiftUI Navigation BarSwiftUI ScrollViewSwiftUI SpacersSwiftUI StacksSwiftUI Views And ModifiersSwiftUI Gestures AdvancedSwiftUI Gestures BasicSwiftUI Gestures CombinedSwiftUI GesturesSwiftUI Custom Row Swipe ActionsSwiftUI HapticsSwiftUI HitTestingSwiftUI InteractionsSwiftUI Searchable ViewsSwiftUI Absolute PositioningSwiftUI AlignmentSwiftUI AlignmentGuideSwiftUI Custom AlignmentSwiftUI GeometryReader BasicsSwiftUI GeometryReader UsageSwiftUI How Layout WorksSwiftUI Layout TechniquesSwiftUI Multiple Views Side by SideSwiftUI Switch View with EnumsSwiftUI Switch View with EnumsSwiftUI NavigationSwift NavigationLinkSwiftUI SheetsSwiftUI TabsSwiftUI BindingSwiftUI Environment WrapperSwiftUI FetchRequest WrapperSwiftUI FocusState WrapperSwiftUI MainActor WrapperSwiftUI ObservableObject WrapperSwiftUI ObservedObject WrapperSwiftUI Property WrappersSwift ObservableObject Manually Publishing ChangesSwiftUI State WrapperSwiftUI StateObject WrapperSwiftUI ViewBuilder WrapperSwiftUI ScenesSwiftUI AlertsSwiftUI Confirmation DialogSwiftUI Context MenuSwiftUI Popup WindowsSwiftUI SheetsCS193p Emoji ArtCS193p Matching GameCS193p Set GameStanford CS193pSwift Basic Data TypesSwift BooleanSwift FloatSwift IntSwift StringSwift ArraySwift ClassSwift Complex Data TypesSwift DictionarySwift EnumSwift SetSwift StructSwift Animating GesturesSwift Animating TransitionsSwift Animations TypesSwift animationsSwift Customize AnimationsSwift URLSessionSwift NetworkingSwift URLSessionSwift Comparable ProtocolsSwift ProtocolsSwift Codable @Published ComformanceSwift CodableSwift Documents DirectorySwift StorageSwift UserDefaultsSwiftSwift App BundleSwift Package DependenciesSwift TimerSwift ToolsSwift Basic TechniquesSwift ClosuresSwift ConditionsSwift ExtensionsSwift FunctionsSwift LoopsSwift OptionalsSwift Variable and ConstantsSwift TechniquesSwift Type AnnotationSwift Unique TypesSwift Result TypeSwift Framework CoreDataSwift Framework CoreImageSwift Framework LocalAuthenticationSwift Framework MLSwift Framework MapKitSwift Framework UNUserNotificationCenterSwift Framework Local NotificationsSwift Framework Remote NotificationsSwift Framework UserNotificationsSwift FrameworksSwiftUI FundamentalsSwiftUI WindowGroupA note about catsConsistency is keyHow to ThinkMove your body every dayYour first seedImage InterpolationCreate accessible spatial experiencesDevelop your first immersive appFundamental Design VisionOSGet started with building apps for spatial...Getting Started visionOSBuild great games for spatial computingCreate a great spatial playback experienceDeliver video content for spatial experiencesDiscover Metal for immersive appsStep Eight visionOSExplore rendering for spatial computingMeet Core Location for spatial computingMeet RealityKit TraceOptimize app power and performance for spatial...Step Five visionOSWhat’s new in Xcode 15Design considerations for vision and motionDesign for spatial inputDesign for spatial user interfacesDesign spatial SharePlay experiencesExplore immersive sound designStep Four visionOSDiscover Quick Look for spatial computingMeet Safari for spatial computingRediscover Safari developer featuresStep Nine visionOSWhat’s new in Safari extensionsBring your Unity VR app to a fully immersive spaceCreate immersive Unity appsExplore App Store Connect for spatial computingStep Seven visionOSExplore materials in Reality Composer ProExplore the USD ecosystemMeet Reality Composer ProStep Six visionOSWork with Reality Composer Pro content in XcodeBuild spatial SharePlay experiencesCreate 3D models for Quick Look spatial...Enhance your iPad and iPhone apps for the Shared...Run your iPad and iPhone apps in visionOSStep Ten visionOSBuilding Spatial Experiences with RealityKitEnhance your spatial computing app with RealityKitEvolve your ARKit app for spatial experiencesMeet ARKit for spatial computingStep Three visionOSElevate your windowed app for spatial computingGo beyond the window with SwiftUIMeet SwiftUI for spatial computingStep Two visionOSTake SwiftUI to the next dimensionTen Steps Overview of visionOS By AppleCreate multiple windows in VisionOSTap and Drag Spatial Gesture in VisionOSVisionOS Basic TutorialsvisionOS Documentation SeriesVisionOS Bear Balloon Reverse Gravity No CollisionVisionOS QuestionsWhy attend WWDCNew to WWDC