Extensions in Swift Explained
Extensions in Swift are super powerful, because they help you organize your code better. You use an extension to add new functionality to an existing class. And they’re especially useful if you don’t have access to the original code of that class!
In this app development tutorial you’ll learn:
- What extensions in Swift are, and why they’re useful
- Common use cases for extensions in practical iOS development
- How you can organize your code better with extensions
- What extensions can’t do, and pitfalls to watch for
Describe your app idea
and AI will build your App
What’s an Extension?
With an extension you can add new functionality to an existing Swift class, struct, enumeration or protocol type. You can literally add new functions to an existing class, even if you don’t have access to the original source code of that class.
Let’s look at an example. Imagine that another developer has defined a class in their code, and you don’t have access to that code. Something like this:
class Airplane
{
var altitude: Double = 0
func setAltitude(feet: Double) {
altitude = feet
}
}
That other developer decided to measure the airplane’s altitude in feet, but you want to set it in meters. You can’t edit their code, and you don’t want to subclass Airplane, so you create a Swift extension instead.
Like this:
extension Airplane
{
func setAltitude(meter: Double) {
altitude = meter * 3.28084
}
}
You can now use the setAltitude(meter:) function on an instance of class Airplane as an ordinary function, like this:
let boeing = Airplane()
boeing.setAltitude(meter: 12000)
print(boeing.altitude) // Output: 39370.08
So, what’s really going on here? When your Swift code compiles, any extensions you’ve defined are added to their respective classes. The original class and its extensions are essentially merged into one. As a result, you can extend a class with new functionality, such as functions.
Here’s what extensions can do in Swift:
- Add functions and computed properties
- Provide new initializers, i.e. constructor functions
- Define subscripts with the subscript() function
- Define and use new nested types, i.e. add a subtype to a type
- Make an existing type conform to a protocol, which is super useful
- Add default implementations to protocols with protocol extensions
Extensions can’t change the fundamental structure of a class (or other type). They can only add functionality, not replace it. As such, you can’t add new properties to an existing type.
Continuing with the previous example, you can’t do this:
extension Airplane
{
var speed: Int = 0
}
Why not? Adding a new property to a class, with an extension, would fundamentally change the structure and needed memory of that class. And we already have a superb way to change the structure of a class: subclassing.
What’s so interesting about extensions is that you can use them to your advantage, to organize your code better. Let’s take a look at these practical use cases for extensions:
- Separating and grouping code
- Using protocol conformance
- Namespaced constants
- Extensions, computed properties and helpers
- Protocol extensions
Separating and Grouping Code with Extensions
A powerful way to use extensions is separating and grouping different parts of a class. You use a base class for the fundamental code of the class, such as its properties, and then add on functions by using extensions.
Let’s take a look at an example:
class Airplane
{
var speed: Double = 0
var altitude: Double = 0
var bearing: Double = 0
}
Then, we define different extensions. First, one for flying the plane:
extension Airplane
{
func changeAltitude(altitude: Double) {
self.altitude = altitude
}
func changeBearing(degrees: Double) {
self.bearing = degrees
}
}
Then, we define functions for take-off and landing:
extension Airplane
{
func takeOff()
{
// Do take-off magic…
}
func land()
{
// Please stow hand luggage and move your seat to an upright position…
}
}
Each extension can have its own Swift file. When you organize a class based on its functionality, and don’t put it all in one file, you can have greater control over where you put what code.
Consider for instance that you’re coding a view controller. The view controller has two main features. You can put each of those features in its own extension, and still use the view controller as one complete class.
The example with the Airplane is of course trivial, but you can imagine that breaking up components in extensions can help you organize a larger code base better. The key is to think about how to organize your code. An approach to start with, would be organizing your code by functionality.
Using Protocol Conformance with Extensions
You can use extensions to conform to a protocol. Like this:
class DetailViewController: UIViewController
{
// Do view controller magic…
}
extension DetailViewController: CLLocationManagerDelegate
{
// Put all location code in here…
}
In the above code, because of the extension, the class DetailViewController now conforms to the CLLocationManagerDelegate delegate protocol. You can then proceed to add the code for that delegate into the extension, separating it from the base class.
When a class conforms to multiple protocols, its class declaration can get messy quickly. And you end up with a huge class, which is generally a bad thing.
Just as with the previous example, you can break up and group a classes code by putting the functionality of each protocol into its own extension. The extension makes the base class adopt a protocol, and you add protocol-specific code to the extension.
Always keep in mind though that extensions shouldn’t be an excuse to skimp on proper app architecture. A huge class separated in 10 extensions is still a huge class, no matter how you slice it.
Namespaced Constants and Nested Types
You can’t add properties to an extension, but you can add static constants and subtypes to extensions. And that lets you do some Swift magic!
Here, consider this example:
extension Notification.Name {
static let statusUpdated = Notification.Name(“status_updated”)
}
The above code extends the Notification.Name type with a static constant called statusUpdated. This class constant is available anywhere on the Notification.Name type, which lets you use it in conjunction with NotificationCenter. Like this:
NotificationCenter.default.post(name: .statusUpdated, object: nil)
The post(name:object:) function expects a value of type Notification.Name as its first parameter. You’ve defined the constant statusUpdated on Notification.Name, so Swift will infer its type, and you can use the shorter .statusUpdated syntax.
You can do a similar thing with subtypes. You can define a subtype in an extension, like this:
extension UserDefaults
{
struct Keys
{
static let useSync = “use_sync”
static let lastSync = “last_sync”
}
}
And you can use that as follows:
UserDefaults.default.set(true, forKey: UserDefaults.Keys.useSync)
This has two benefits:
- The Keys subtype is only available on the UserDefaults type, which means that you won’t have an arbitrary struct defined at a global level in your code. This is similar to class-level namespacing in other programming languages – something that Swift cannot do.
- The use of constants simply means that you’re less likely to make errors when typing “use_sync” for the UserDefaults key. You can use the same concept anywhere you use static keys.
Extensions, Computed Properties and Helpers
Swift is a super useful programming language, but sometimes it just doesn’t have what you need.
Consider for instance that prior to Swift 4.2, arrays had no simple shuffled() function to randomize arrays. It’s lunacy to subclass arrays just to be able to randomize them, so you resort to using a global helper function.
What if you could add that helper function directly to the Array type? Like this:
extension Array
{
func shuffled() {
// Shuffle array items and return shuffled array
}
}
The above code merely adds a shuffled() function to the Array type. As a result, that function is available on any array. Neat!
Here’s another helpful example:
class Circle {
var radius: Double = 0
}
extension Circle
{
var circumference:Double {
return radius * .pi * 2
}
}
The above code first declares a Circle class, and then adds a circumference computed property with an extension. When a computed property is accessed (see below), the code within the squiggly brackets is executed and a value is returned.
And calculate the circumference of a circle like this:
let circle = Circle()
circle.radius = 10
print(circle.circumference) // Output: 62.83185307179586
Again, a trivial example, but imagine you don’t have access to the source code of the original Circle class, or that you want to organize its functionality better.
You can constrain an extension to a specific type or protocol by using the where keyword. Learn more here: How to Use “where” in Swift
Protocol Extensions
The last approach we’ll discuss is using protocol extension. Unlike protocol conformance (see above), protocol extensions let you directly extend a protocol. What’s really mind-boggling is that you can provide default implementations of protocols by using extensions.
Consider this simple protocol:
protocol Edible {
func eat()
}
The Edible protocol defines a function eat(), so any class that wants to conform to the protocol needs to implement the eat() function. Something like this:
class Fish: Edible
{
func eat() {
print(“**CHOMP** **CRUNCH** Eating the fish…”)
}
}
So far so good! With protocol extensions, you can now extend the protocol to include a default implementation. Like this:
extension Edible
{
func eat() {
print(“Eating the thing…”)
}
}
Note that the above code extends the protocol type Edible, and not the Fish class. Because of that protocol extension, any class that adopts the Edible protocol can now use the default implementation of eat(). Like this:
class Apple: Edible {
// Do nothing…
}
let apple = Apple()
apple.eat() // Output: Eating the thing…
See how we’re calling the eat() function on apple, even though the Apple class does not provide an implementation? That’s because of the default implementation from the protocol extension. Awesome!
Further Reading
Extensions increase the composability of your code, they let you add functionality to classes you can’t change, and help you organize your code better. And they’re downright awesome!
Related Articles
- What is Facebook Messenger App | Complete Guide 2021
- Best Slack Slash Commands to Boost your Productivity
- Workflow Automation Planning: Tips, Tricks & Strategies for Efficient Automated Workflows
- How to generate business ideas? [+Top Startup Ideas for 2022]
- Best Design Tools to Create Stunning Social Media Graphics
- How to create an email newsletter?
- How E-Commerce Companies Can Overcome Current Challenges (Podcast 107)
- Self and self in Swift
- What are the Best Practices for MVP Development?
- Major Airtable Integrations to Help You Enhance Your Business Workflows
Most Popular Posts
- QRgen’s QR Code Generator Review
By Deepak Kumar | October 14, 2024
- Google Sheets Review: My Experience with a Collaborative Powerhouse
By Tanya | October 11, 2024
- Emojifyer Emoji Generator Review: Pros, Cons, & Pricing
By Deepak Kumar | October 10, 2024
- Vagaro Salon Software Review
By Yuvraj Singh | October 10, 2024
- Why Adobe Commerce (Magento 2.X) is the Future of E-commerce
By Tanya | October 10, 2024