DEV Community

Gamya
Gamya

Posted on

Swift Type Annotations

So far in this series, Swift has been quietly figuring out what type of data we're working with — and it's been doing a great job. But sometimes you need to take the wheel and tell Swift exactly what type you want. That's where type annotations come in.


🧠 Type Inference — Swift's Default Behaviour

When you write this:

let characterName = "Itachi"
var killCount = 0
Enter fullscreen mode Exit fullscreen mode

Swift looks at the values and makes a smart guess:

  • "Itachi" → must be a String
  • 0 → must be an Int

This is called type inference — Swift infers the type from the value you assign. Most of the time this works perfectly and you don't need to do anything extra.


✍️ Type Annotations — Being Explicit

Type annotations let you tell Swift directly what type a variable or constant should be, using a colon after the name:

let characterName: String = "Itachi"
var killCount: Int = 0
Enter fullscreen mode Exit fullscreen mode

Both of these do the same thing as before — but now you're the one specifying the type, not Swift. That's the key difference.


🤔 So Why Would You Ever Need This?

There are three main situations where type annotations become useful:

1️⃣ Swift can't figure out the type itself

This usually happens with more advanced code — like when you're loading data from an API or doing something complex where Swift doesn't have enough context to make a decision on its own. You step in and tell it.

2️⃣ You want a different type than Swift's default

This is the most common beginner use case. Consider this:

var powerLevel = 0
Enter fullscreen mode Exit fullscreen mode

Swift sees 0 and assumes Int. But what if power levels in your app can be 99.5? You'd need a Double. Without a type annotation, you'd have to write:

var powerLevel = 0.0
Enter fullscreen mode Exit fullscreen mode

Which works, but with a type annotation it reads much more clearly:

var powerLevel: Double = 0
Enter fullscreen mode Exit fullscreen mode

Now powerLevel is definitely a Double — even though the starting value looks like a whole number. Much more intentional and readable ✅

3️⃣ You want to declare a variable without giving it a value yet

Sometimes you know a variable is coming, but you don't have the value for it just yet. Type annotations make this possible:

var winnerName: String

// ... lots of battle logic ...

winnerName = "Levi Ackerman"

print(winnerName)
Enter fullscreen mode Exit fullscreen mode

Swift is totally fine with this — as long as you provide the value before you use the variable. If you forget to assign it before using it, Swift will refuse to compile. And if you try to change it to a different type later, Swift will also refuse. Safe on all sides. 🔒


📚 Type Annotations for Every Swift Type

Here's a handy reference for all the types we've covered in this series, with their type annotation syntax:

Type Annotation Example
Text String let name: String = "Gojo"
Whole number Int var level: Int = 1
Decimal number Double let height: Double = 1.93
True / False Bool var isAlive: Bool = true
Array of strings [String] var moves: [String] = ["Rasengan"]
Dictionary [String: Int] var scores: [String: Int] = [:]
Set of strings Set<String> var titles: Set<String> = []

🏗️ Creating Empty Collections with Type Annotations

This is where type annotations become truly necessary — when you want to start with an empty collection and fill it in over time.

If you try to create an empty array without a type annotation, Swift has no idea what kind of data it will hold:

var jutsuList = [] // ❌ Swift doesn't know what type this is
Enter fullscreen mode Exit fullscreen mode

You need to tell it:

var jutsuList: [String] = []
Enter fullscreen mode Exit fullscreen mode

Or using Swift's shorthand:

var jutsuList = [String]()
Enter fullscreen mode Exit fullscreen mode

Both are valid — pick whichever feels more readable to you.

Here's a real example of why you'd want to start empty. Say you have a list of all ninja in a village, and you want to filter out just the ones in Squad 7:

let allNinja = ["Naruto", "Sasuke", "Sakura", "Kakashi", "Sai", "Yamato"]

var squadSeven: [String] = []  // Start empty

for ninja in allNinja {
    if ninja == "Naruto" || ninja == "Sasuke" || ninja == "Sakura" {
        squadSeven.append(ninja)
    }
}

print(squadSeven)
// ["Naruto", "Sasuke", "Sakura"]
Enter fullscreen mode Exit fullscreen mode

You didn't know which names would make the cut ahead of time — so you started empty and built it up as you went. This pattern shows up constantly in real apps. 💡

The same idea applies to dictionaries and sets:

var characterStats: [String: Int] = [:]       // Empty dictionary
var uniqueAbilities: Set<String> = []          // Empty set
Enter fullscreen mode Exit fullscreen mode

⚖️ Type Inference vs Type Annotation — Which Should You Use?

Honestly? This comes down to personal style. Here's how to think about it:

Situation Recommended approach
You have an initial value and Swift guesses correctly ✅ Use type inference — keep it short
You want a different type than Swift's default ✅ Use type annotation
You don't have a value yet ✅ Must use type annotation
Your team has a strict style guide Follow the guide

In practice, most Swift developers prefer type inference wherever possible — it keeps code shorter and easier to read. But knowing type annotations cold is essential, because you will need them.


⛔ One Hard Rule — Types Can Never Change

No matter which approach you choose, Swift has one rule that never bends:

A variable's type is set once and never changes.

This means you can't do this:

var chakraLevel = 100     // Swift infers Int
chakraLevel = "Maximum"  // ❌ ERROR — can't assign a String to an Int
Enter fullscreen mode Exit fullscreen mode

And you can't trick it with annotations either:

let score: Int = "Zero"  // ❌ ERROR — "Zero" is not an Int, period
Enter fullscreen mode Exit fullscreen mode

Swift just can't convert "Zero" to an integer, no matter what annotation you use. The annotation has to be truthful about the type you're actually storing. This is what makes Swift a type-safe language — it stops nonsense like adding a number to a boolean (5 + true) before your app even runs. 🛡️


🧩 Putting It All Together

Here's a mini character builder showing type annotations in action:

// Using type annotations explicitly
let heroName: String = "Naruto Uzumaki"
var currentRank: String = "Genin"
var chakra: Double = 100.0
var isHokage: Bool = false
var knownJutsu: [String] = []
var stats: [String: Int] = [:]

// Fill things in
knownJutsu.append("Shadow Clone Jutsu")
knownJutsu.append("Rasengan")
knownJutsu.append("Sage Mode")

stats["speed"] = 90
stats["strength"] = 85
stats["intelligence"] = 70

// Years later...
isHokage = true
currentRank = "Hokage"

print("\(heroName) is now \(currentRank)!")
print("Jutsu count: \(knownJutsu.count)")
print("Is Hokage: \(isHokage)")
Enter fullscreen mode Exit fullscreen mode

Output:

Naruto Uzumaki is now Hokage!
Jutsu count: 3
Is Hokage: true
Enter fullscreen mode Exit fullscreen mode

🌟 Wrap Up

Type annotations are Swift's way of letting you be explicit about what your variables hold. Here's what to remember:

  • Type inference — Swift figures out the type from the value you give it. Clean and short.
  • Type annotation — You tell Swift the type with : TypeName. Explicit and intentional.
  • Use annotations when Swift guesses wrong, when you need an empty collection, or when you're declaring a variable without a value yet.
  • A variable's type is set once and never changes — that's Swift's type safety in action.
  • Both approaches are valid — use whichever makes your code clearer in the moment.

Mastering types is one of the foundational skills in Swift — everything else you'll build on top of this. 💪

Top comments (0)