Types and Type Inference
Swift is a strongly typed language with a rich type system, supporting value types (structs, enums) and reference types (classes). Type inference minimizes explicit type declarations while maintaining safety.
Built-in Types
Swift provides a comprehensive set of built-in types.
Numeric Types
- Integers:
Int
(platform-dependent, typically 64-bit),UInt
,Int8
,Int16
,Int32
,Int64
. - Floating-Point:
Double
(64-bit),Float
(32-bit).
Example:
let count: Int = 42
let price: Double = 9.99
let smallValue: Int8 = 127 // Max for Int8
Boolean Type
Bool
:true
orfalse
.
Example:
let isActive: Bool = true
Text Types
- Character: Single Unicode grapheme cluster.
- String: Collection of characters.
Example:
let letter: Character = "A"
let greeting: String = "Hello, Swift!"
Collection Types
Array
: Ordered list.Dictionary
: Key-value pairs.Set
: Unordered unique elements.
Example:
let numbers: [Int] = [1, 2, 3]
let scores: [String: Int] = ["Alice": 90]
let colors: Set<String> = ["Red", "Blue"]
Tuple Type
Groups multiple values, optionally named.
Example:
let point: (x: Double, y: Double) = (1.0, 2.0)
let status: (Int, String) = (200, "OK")
print(point.x, status.0) // 1.0, 200
Function Types
Represent function signatures.
Example:
let operation: (Int, Int) -> Int = (+)
print(operation(5, 3)) // 8
Type Aliases
Define alternative names for types.
Example:
typealias Coordinate = (Double, Double)
typealias JSON = [String: Any]
let origin: Coordinate = (0.0, 0.0)
Nested Types
Define types within structs, classes, or enums.
Example:
struct Card {
enum Suit: String {
case spades, hearts, diamonds, clubs
}
struct Rank {
let value: Int
}
let suit: Suit
let rank: Rank
}
let aceOfSpades = Card(suit: .spades, rank: .init(value: 1))
Type Inference
Swift infers types from initial values, function return types, or context.
Example: Variable Inference:
let count = 10 // Inferred as Int
let name = "Swift" // Inferred as String
let values = [1.0, 2.0] // Inferred as [Double]
Example: Function Inference:
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
let result = add(2, 3) // Inferred as Int
Example: Closure Inference:
let sorted = [3, 1, 2].sorted { $0 < $1 } // Inferred as [Int]
Type Safety
Swift enforces type correctness at compile time.
Example:
let number: Int = 42
// number = "Hello" // Error: Cannot assign value of type 'String' to type 'Int'
Type Casting
Use as
, as?
, or as!
for type conversion.
Example:
let value: Any = 42
if let intValue = value as? Int {
print("Integer: \(intValue)")
}
let forced = value as! Int // Crashes if not Int
Type Erasure
Hide specific types while preserving protocol conformance.
Example:
protocol Shape {
func draw()
}
struct AnyShape: Shape {
private let _draw: () -> Void
init<T: Shape>(_ shape: T) {
_draw = shape.draw
}
func draw() { _draw() }
}
let shapes: [AnyShape] = [AnyShape(Circle()), AnyShape(Rectangle())]
Metatype Types
Refer to the type itself using .Type
or .self
.
Example:
let type: String.Type = String.self
let instance = type.init("Hello")
print(instance) // "Hello"
Best Practices
- Specific Types: Use
Int32
orFloat
only when required (e.g., interop). - Type Inference: Rely on inference for local variables, annotate for clarity in APIs.
- Type Aliases: Use for complex types to improve readability.
- Type Safety: Avoid
Any
unless necessary (e.g., JSON parsing). - Nested Types: Organize related types within parent types.
- Type Casting: Prefer conditional casting (
as?
) over forced (as!
).
Troubleshooting
- Type Mismatch: Check inferred vs. expected types.
- Ambiguous Inference: Add type annotations to resolve conflicts.
- Casting Failures: Verify types with
is
before casting. - Performance Issues: Minimize use of
Any
or dynamic typing.
Example: Comprehensive Type Usage
typealias Point = (x: Double, y: Double)
struct Geometry {
enum ShapeType {
case circle(radius: Double)
case rectangle(width: Double, height: Double)
}
static let origin: Point = (0.0, 0.0)
var shapes: [AnyShape]
var coordinates: [Point]
func addShape(_ shape: ShapeType) -> String {
switch shape {
case let .circle(radius):
return "Circle with radius \(radius)"
case let .rectangle(width, height):
return "Rectangle with area \(width * height)"
}
}
}
let geometry = Geometry(shapes: [], coordinates: [(1.0, 2.0)])
print(geometry.addShape(.circle(radius: 5.0))) // "Circle with radius 5.0"