In this tutorial, we’ll walk you through the basics of extending in Swift. We’ll demonstrate how the Swift extension works by building a simple exercise tracking application.

We will highlight the following.

  • What is Swift Extension?
  • Create an extension in Swift
  • The type attribute
  • Variation method
  • The separation of code
  • Expand on SwiftUI view
  • Adds an initializer for an existing type

What is Swift Extension?

Extensions, well, extend existing Swift-named types — that is, structures, classes, enumerations, and protocols — so you can add more functionality to them. This enables you to insert your own code into existing system code that you would otherwise not be able to access, such as the Foundation framework. You can also use extensions to extend and clean your own code.

Create an extension in Swift

Creating an extension is similar to creating a named type in Swift. When you create an extension, you add extension before the name.

extension SomeNamedType {
  // Extending SomeNamedType, and adding new
  // functionality to it.
}

Copy the code

The type attribute

You can extend a particular named type, add a new instance, and assign type attributes to it. For example, you can extend Color and add your own colors to it. Let’s say our app has a brand color that we want to use everywhere. We can extend Color with extension to create a constant type attribute, brand.

Our application also uses custom colors for row backgrounds in the Settings screen. To do this, we will define a variable type property that adjusts the color based on the appearance of the system.

extension Color {
  static let brand = Color(red: 75/255, green: 0, blue: 130/255)

  static var settingsBackground: Color {
    Color(UIColor { (trait) -> UIColor in
      return trait.userInterfaceStyle == .dark ? .systemGray5 : .systemGray6
    })
  }
}

Copy the code

Here’s how to use it.

struct SettingsRow: View {
  var title: String

  var body: some View {
    HStack(spacing: 8) {
      Text(title)
        .foregroundColor(.brand)

      Spacer()

      Image(systemName: "chevron.right")
    }
    .foregroundColor(.settingsBackground)
  }
}

Copy the code

Variation method

As mentioned in the introduction, you can extend types to add your own functionality, even if you don’t have access to the original code base. For example, if you want to give a Double, you can write an extension on the structure without having to access the original code of the Double structure.

In our application, we are getting calorie data from HealthKit, but the function returns data of type Double. We want to display data rounded to one decimal point. We can write an extension in Double like this.

extension Double { mutating func roundTo(places: Int) {let divisor = pow(10.0, Double(places)) self = (self * divisor).rounded()/divisor}}Copy the code

Let’s use this mutation method to solve our problem.

var caloriesBurned: Double? = 213.3244 if var calorie = caloriesBurned {calorie. RoundTo (places: 1) print("\(calories) kcal") // Prints "213.3kcal"}Copy the code

The separation of code

When our class is protocol compliant, we usually add all the protocol methods in the same class. For example, we will add UICollectionViewDataSource, UICollectionViewDelegate and UICollectionViewDelegateFlowLayout all methods.

We can use extensions to isolate each of the required methods. This makes the code more readable and easier to maintain.

class ExampleViewController: UIViewController {
    // Add the main code goes here
}

// MARK:- UICollectionViewDataSource
extension ExampleViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        //
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        //
    }
}

// MARK:- UICollectionViewDelegate
extension ExampleViewController: UICollectionViewDelegate {
    //
}

// MARK:- UICollectionViewDelegateFlowLayout
extension ExampleViewController: UICollectionViewDelegateFlowLayout {
    //
}

Copy the code

Our application uses Google Sign-in as the primary authentication source, so we need to conform to GIDSignInDelegate to receive updates after a successful login. We can separate the code needed to do this — you guessed it — with extensions.

import GoogleSignIn class AuthenticationViewModel: NSObject, ObservableObject { /// Main code goes here } // MARK:- GIDSignInDelegate extension AuthenticationViewModel: GIDSignInDelegate { func sign(_ signIn: GIDSignIn! , didSignInFor user: GIDGoogleUser! , withError error: Error!) { if error == nil { // Authentication successful } else { print(error.debugDescription) } } }Copy the code

Extension on SwiftUI View

Now, let’s say we want to add a custom headline text, like the one Apple uses for headlines in most of its applications. This text will indicate the date of a workout. We also want to use the exact custom text on the Settings screen.

To reuse this code in the code base, we’ll extend Text to add a largeTitle(:) method.

extension Text {
    func largeTitle() -> some View {
        self
            .bold()
            .foregroundColor(.primary)
            .font(.largeTitle)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.top, 37)
    }
}

Copy the code

Now we can use this method on our view.

VStack {
    Text("Settings").largeTitle()
}

Copy the code

Similarly, suppose we want to create a heart-shaped button for a collection of exercises. We will create a ViewModifier that switches the color of the heart on a double click.

struct HeartButtonModifier: ViewModifier {
    @Binding var state: Bool

    func body(content: Content) -> some View {
        content
            .foregroundColor(state ? .red : .secondary)
            .onTapGesture(count: 2) {
                state.toggle()
            }
    }
}

Copy the code

Now let’s create an extension on the View so that we can use it in our View.

extension View {
    func workoutLiked(state: Binding<Bool>) -> some View {
        self.modifier(HeartButtonModifier(state: state))
    }
}

Copy the code

Finally, we add it to the Image as a modifier.

struct LikeView: View {
    @State private var state = false

    var body: some View {
        Image(systemName: "heart.fill")
            .workoutLiked(state: $state)
    }
}

Copy the code

Adds an initializer for an existing type

We can use an extension to add a new custom initializer to an existing type that accepts different parameters.

Let’s assume that your designer gives you a hexadecimal color instead of an RGB value. Using the previous example of adding a calculated type attribute to Color, we will create an initializer that accepts hexadecimal values. If we want RGB values to be integer colors, we can add another initializer.

extension Color { init(hex: Int) { let red = (hex >> 16) & 0xFF let green = (hex >> 8) & 0xFF let blue = hex & 0xFF self.init(red: red, green: green, blue: blue) } init(red: Int, green: Int, blue: Int) { let red = Double(red) / 255 let green = Double(green) / 255 let blue = Double(blue) / 255 self.init(red: Opacity: 1.0)}}Copy the code

We can now use it as a.

extension Color {
    static var brand: Color {
        Color(hex: 0x4B0082)
    }

    static var secondaryBrand: Color {
        Color(red: 41, green: 0, blue: 71)
    }
}

Copy the code

conclusion

Extensions in Swift are a powerful way to add your own functionality to types that don’t belong to you. This overview of extensions and the examples here are intended to help you understand how extensions work so that you can implement and use them in your own Swift projects.

For further reading, I recommend the following article in the Swift documentation.

  • “Add protocol consistency with extensions”
  • “Extensions using generic Where statements”

The postSwift Extensions: An overview with examples appears on The LogRocket blog.