Programming in Swift: Functions & Types

Jan 4 2022 · Swift 5.5, iOS 15, Xcode 13

Part 1: Functions

04. Overloading

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 03. Challenge: Functions Next episode: 05. Advanced Parameters

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 04. Overloading

Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.

Transcript: 04. Overloading

In the previous episode, we reviewed several functions that shared the same name. When you create multiple functions with the same name, it’s called “Overloading”.

Overloads are unique to functions! Other named types, like structures and classes, require a unique name. But there are still some rules you need to follow when creating function overloads. Let’s write some examples together!

When you call overloaded functions, the Swift compiler has to have a way to tell them apart. To do that, any overloads must have a difference in the functions’ parameter lists or return types.”

To create some examples, let’s start with a getPassStatus function from the Programming in Swift: Fundamentals course:

func getPassStatus(for grade: Int) -> Bool {
  grade >= passingGrade
}

One way to implement overloading is to use a different number of parameters. I can do that by adding a lowestPass parameter.

func getPassStatus(for grade: Int, lowestPass: Int ) -> Bool {
  grade >= lowestPass
}

Now, if you wanted to use a different lowest passing grade, you could use the second function…

getPassStatus(for: ozmaGrade, lowestPass: 88)

But if you just want to assume 50 is the lowest passing grade, you could use the first function…

getPassStatus(for: jessyGrade)

You’ve already seen another way to get essentially the same result, though. Instead of overloading and writing two functions, you can use one function and give lowestPass a default value.

func getPassStatus(for grade: Int, lowestPass: Int = passingGrade ) -> Bool {
  grade >= lowestPass
}

This will show up as two functions in your autocomplete list, but it isn’t really overloading. I recommend using default values instead of overloads, when you can. It will probably make your code easier to maintain.

Another way you can create an overload is to use different types of parameters. For example, what if you wanted to get the passing status based on the average of multiple grades? Your function might still be named getPassStatus

func getPassStatus
  
}

But this one would take an array of Ints.

func getPassStatus(for grades: [Int]) -> Bool {
  
}

And then you could calculate the average in the function body. I’ll use a for loop to iterate over the array of grades and add them up…

func getPassStatus(for grades: [Int]) -> Bool {
  var totalGrade = 0
  for grade in grades {
    totalGrade += grade
  }
}

Then divide the total by the number of grades to get the average grade…

  let averageGrade = totalGrade / grades.count
}

And then find out if that average is a passing grade.

  return averageGrade >= passingGrade
}

Call that function and pass in allll of Ozma’s grades, and notice that the argument label is the same for both functions.

getPassStatus(for: ozmaAllGrades)

The only distinction you have here at the call site is the parameter type!

That’s why you can’t create overloads that just have different parameter names. But you can overload functions by using different argument labels. There’s a great example in Swift, already! The stride functions.

stride lets you create a sequence of values that skip a certain amount between each value. For example, you can create a sequence of numbers between 10 and 0, stepping through two at a time.

stride(from: 10, to: 0, by: -2)

You can use stride similar to how you’d use a range…

for i in stride(from: 10, to: 0, by: -2) {
  print(i)
}

Notice that the printed results of stride(to:) don’t include 0. But there is an overload that uses a different argument label for that second parameter, stride(through)…

for i in stride(from: 10, through: 0, by: -2) {
  print(i)
}

Now you can see how these two functions work a bit like ranges. stride(to) doesn’t include the last value, but stride(through) does!

So, in this case, the argument labels describe an important difference in the way the functions work. You may have noticed that stride is a way to create a sequence of numbers that go down in value. That’s something you can’t do with ranges!

The last way to overload functions is to use a different return type. If you have a function that returns one type, like an Int…

func getValue() -> Int {
  return 13
}

And another with the same name that returns a String…

func getValue() -> String {
  "meow"
}

The compiler will have no idea which function you mean to call if you try to rely of type inference:

let value = getValue()

You would need to explicitly state the type you’re looking for.

let intValue: Int = getValue()

To review, when functions share a name, there must be some difference in their parameter lists or the return types. Otherwise your code won’t compile!

Valid overloads can have a different number of parameters, have different parameter types, use different argument labels or have different return types.

There are also some guidelines you should follow when considering overloading:

  • Functions that share a name should be related and have similar functionality!
  • When you can, give parameters default values instead of adding overloads with a different number of parameters.
  • Be extra cautious about creating overloads only through different return types. You lose type inference, so it’s not recommended.

In general, just be thoughtful about creating overloads.