Types & Operations: Strings, Tuples, and Type Inference

Swift Chapter 4 of the Ultimate Swift Series 28 min read April 10, 2026 Beginner

In This Article

  1. Type Conversion: Being Explicit
  2. Working With Mixed Types
  3. Type Inference: Let Swift Figure It Out
  4. Strings: Working With Text
  5. String Interpolation
  6. Multi-line Strings
  7. Tuples: Grouping Related Values
  8. The Full Range of Number Types
  9. Type Aliases
  10. A Peek at Protocols
  11. Exercises
  12. Key Points

In the previous chapter, you worked with integers and doubles. But Swift has a rich type system that goes far beyond numbers. In this chapter, you'll learn how types interact with each other, how Swift can infer types for you, and how to work with text using strings. You'll also discover tuples — a simple way to group related values together.

Type Conversion: Being Explicit

Swift is strict about types. You can't accidentally mix them. Consider this:

var integer: Int = 100 var decimal: Double = 12.5 integer = decimal // ERROR: Cannot assign value of type 'Double' to type 'Int'

Many languages would silently convert this for you. Swift refuses. Why? Because silent conversions are a major source of bugs. When you convert a Double to an Int, you lose the decimal part. Swift wants you to acknowledge that loss explicitly:

integer = Int(decimal) // Now integer = 12 (the .5 is gone)

By wrapping the value in Int(), you're telling Swift: "Yes, I know I'm losing precision. Do it anyway." This explicitness prevents an entire category of subtle bugs.

Working With Mixed Types

The same strictness applies to operations. You can't multiply an Int by a Double directly:

let hourlyRate: Double = 19.5 let hoursWorked: Int = 10 let totalCost = hourlyRate * hoursWorked // ERROR!

Swift makes you choose: should the result be a Double (preserving the decimal) or an Int (rounding down)? Almost always, you want to preserve precision:

let totalCost = hourlyRate * Double(hoursWorked) // 195.0

Convert the less precise type to the more precise one. Converting the Int to a Double loses nothing. Going the other way would round 19.5 down to 19.

Type Inference: Let Swift Figure It Out

You've been writing type annotations on every declaration. But Swift's compiler is smart enough to figure out types on its own:

let age = 42 // Swift infers Int let pi = 3.14159 // Swift infers Double let name = "Swift" // Swift infers String

This is called type inference. Swift looks at the value you're assigning and determines the type automatically. The result is identical to writing the type explicitly — it's just less typing.

Checking inferred types in Xcode

In a playground, hold Option and click on any variable or constant name. Xcode will show you a popover with the inferred type. This is invaluable when you're not sure what type Swift chose.

Sometimes you want a specific type that differs from what Swift would infer. There are three ways to handle this:

// Swift infers Int, but you want Double let wantADouble = 3 // This is an Int! // Option 1: Use a type annotation let option1: Double = 3 // Option 2: Convert explicitly let option2 = Double(3) // Option 3: Use the 'as' keyword let option3 = 3 as Double // Simplest: just use a decimal point let simplest = 3.0 // Swift infers Double

Strings: Working With Text

Numbers are just the beginning. Most apps deal heavily with text — names, messages, labels, URLs. In Swift, text is represented by the String type.

How computers store text

At the hardware level, computers only understand numbers. So every character maps to a number via a standard called Unicode. The letter "a" is 97, "b" is 98, and so on. Unicode covers every writing system on Earth — Latin, Chinese, Arabic, Cyrillic — plus emoji. The character "🐶" is code point 128054.

You don't need to think about code points in daily programming. Swift handles all the encoding automatically. But knowing it's all numbers underneath helps you understand why strings behave the way they do.

Characters and Strings

let singleCharacter: Character = "a" // One character let emoji: Character = "🐶" // Emoji is also one character let word: String = "Swift" // Multiple characters let inferred = "Hello" // Swift infers String

Character holds exactly one character. String holds zero or more. Swift always infers string literals as String, so you need an explicit type annotation if you want a Character.

Combining strings

You can join strings together using the + operator, just like adding numbers:

var message = "Hello" + " my name is " let name = "Alex" message += name // "Hello my name is Alex" let exclamation: Character = "!" message += String(exclamation) // "Hello my name is Alex!"

Notice that adding a Character to a String requires explicit conversion — the same type strictness you saw with numbers.

String Interpolation

Concatenation works, but there's a better way to build strings with dynamic values. String interpolation lets you embed values directly inside a string:

let name = "Alex" let age = 28 let greeting = "Hi, I'm \(name) and I'm \(age) years old." // "Hi, I'm Alex and I'm 28 years old."

Use \(expression) inside a string literal to insert any value. This works with all types — strings, numbers, booleans, even expressions:

let price = 9.99 let quantity = 3 let receipt = "Total: $\(price * Double(quantity))" // "Total: $29.97"

String interpolation is one of Swift's most-used features. It's cleaner than concatenation and handles type conversion automatically.

Multi-line Strings

For longer text, Swift supports multi-line string literals using triple quotes:

let poem = """ Roses are red, Violets are blue, Swift is great, And so are you. """

The indentation of the closing """ determines the baseline. Swift strips that many leading spaces from every line, so your code can be nicely indented without affecting the output. The first and last newlines (right after and before the triple quotes) are not included in the string.

Tuples: Grouping Related Values

Sometimes you need to group a few values together without creating a full type. That's what tuples are for. They're lightweight containers for related data:

let coordinates = (2, 3) // Type: (Int, Int) let mixed = (2.1, 3) // Type: (Double, Int) let person = ("Alex", 28, 5.11) // Type: (String, Int, Double)

Access values by position (starting from 0):

let x = coordinates.0 // 2 let y = coordinates.1 // 3

Named tuples

Position-based access is unclear. Named tuples are much better:

let point = (x: 2, y: 3) let px = point.x // 2 — much clearer than point.0 let py = point.y // 3

Decomposing tuples

You can extract multiple values at once:

let point3D = (x: 2, y: 3, z: 1) // Extract all three let (px, py, pz) = point3D // Ignore what you don't need with _ let (justX, justY, _) = point3D // Ignores z

The underscore (_) is Swift's "I don't care about this value" operator. You'll see it throughout the language.

When to use tuples

Tuples are great for returning multiple values from a function or grouping temporary data. For anything more complex or reusable, you'll want a struct (covered in a later chapter). Think of tuples as quick, disposable groupings.

The Full Range of Number Types

You've been using Int and Double. Swift actually has many more numeric types, each with different storage sizes and ranges:

Signed integers (can be negative): Int8, Int16, Int32, Int64

Unsigned integers (zero and positive only): UInt8, UInt16, UInt32, UInt64

Decimals: Float (4 bytes, ~6 digits precision) and Double (8 bytes, ~15 digits precision)

Int is 64-bit on modern hardware. Double is the default for decimals. Use these two 99% of the time. The specific-size types exist for interoperability with other systems or when optimizing memory.

When mixing specific number types, convert to a common type:

let a: Int16 = 12 let b: UInt8 = 255 let c: Int32 = -100000 let result = Int(a) + Int(b) + Int(c) // -99733

Type Aliases

You can create custom names for existing types using typealias. This doesn't create a new type — it's just a more readable name:

typealias Coordinate = (Int, Int) let point: Coordinate = (5, 10) typealias Temperature = Double let boilingPoint: Temperature = 100.0

Type aliases shine when working with complex types. They make your code self-documenting without adding overhead.

A Peek at Protocols

You might wonder: with so many number types, how does Swift keep them manageable? The answer is protocols — one of Swift's most powerful features.

A protocol defines a set of operations that a type must support. All integer types conform to the BinaryInteger protocol, which itself conforms to Numeric. This means once you know how to use one integer type, you know how to use them all — they share the same operations.

You'll dive deep into protocols later in this series. For now, just know that Swift organizes types by their shared behaviors, and this is what makes the type system feel consistent rather than overwhelming.

Exercises

Try These in Your Playground

  1. Create constants age1 = 42 and age2 = 21. Calculate their average using (age1 + age2) / 2. What's wrong with the result? Fix it using Double conversion.
  2. Create a firstName and lastName constant. Build a fullName string using concatenation, then build an introduction using string interpolation.
  3. Create a named tuple representing today's date with month, day, year, and avgTemperature. Extract just the day and avgTemperature into constants using decomposition (ignore the rest with _).
  4. What is the value of "\(10) multiplied by \(5) equals \(10 * 5)"?
  5. Given let a = 4, let b: Int32 = 100, let c: UInt8 = 12 — what is Int(a) + Int(b) - Int(c)?
  6. Bonus: What is the numeric difference between Double.pi and Float.pi? Try it in a playground.

Key Points

What You Learned

In the next chapter, we'll cover Control Flow — how to make decisions with if/else, pattern match with switch, and repeat work with loops.

See these concepts in action

Our Swift Fundamentals video course covers variables, types, strings, and more with 96 video lessons.

Watch Swift Videos