Swift – Apple’s slick and safety-focused (? still not sure I’m bought in on that phrasing) programming language – has become the backbone of Apple’s development. Whether you’re hacking on an indie app or shipping production-grade enterprise software, writing consistent, readable, and idiomatic Swift is non-negotiable. Swift has a robust set of guidelines to follow, and I wrote a thing or three about it’s style of OOP, POP, and FP rolled into one language here, but let’s cut through the noise and hit the key conventions that will level up your Swift code.
Naming Things Well
Naming in Swift follows camelCase convention. Class names and structs should use UpperCamelCase, while variables and function names stick to lowerCamelCase.
Example:
struct Spaceship { }
let maxSpeed = 9000
func accelerate(to speed: Int) { }
Apple’s naming conventions emphasize clarity and intent over brevity. No cryptic abbreviations.
- Avoid:
let ms = 9000 - Do:
let maxSpeed = 9000
Constants Over Variables
Swift encourages immutability. Use let instead of var wherever possible to make your code safer and more predictable.
Example:
let planetsInSolarSystem = 8 // Immutable
var spaceshipFuel = 100 // Only use 'var' when mutation is required
Learn more about constants and variables in the Swift Language Guide.
Function Signatures
Functions should be descriptive yet concise, using parameter labels to improve readability.
Example:
func fly(to destination: String) { }
Compare that to:
func fly(_ d: String) { } // Hard to understand
More details are available in Apple’s Swift Function Guide.
Structs vs. Classes
Swift loves value types. Use structs over classes whenever possible because they’re safer and don’t come with reference-sharing side effects.
| Use Structs When | Use Classes When |
|---|---|
| Data is immutable or simple | Objects require identity |
| No need for inheritance | Need to subclass |
| Value semantics (copy behavior) | Reference semantics (shared instance) |
Apple provides more details on value vs. reference types.
Optionals and Safely Handling Nil
Swift forces you to think about nullability upfront with optionals (?) and safely unwrapping values.
Example:
var optionalSpaceship: Spaceship?
if let spaceship = optionalSpaceship {
print("We have a spaceship: \(spaceship)")
} else {
print("No spaceship available.")
}
Unwrapping can be done with if let, guard let, or force-unwrapping (!)—though force unwrapping should only be used when you’re absolutely sure the value exists. For more on this, see Optionals.
Guard for Early Exits
Instead of piling on nested if statements, Swift encourages using guard for cleaner flow control.
Example:
func launch(spaceship: Spaceship?) {
guard let spaceship = spaceship else {
print("No spaceship available!")
return
}
print("Launching \(spaceship)")
}
This keeps the happy path unindented and prevents excessive nesting. More on guard.
Protocols Over Inheritance
Inheritance leads to tight coupling. Swift encourages protocol-oriented programming, where behavior is shared via protocols instead of deep class hierarchies.
Example:
protocol Flyable {
func fly()
}
struct Rocket: Flyable {
func fly() { print("🚀 Rocket launching!") }
}
Protocols help keep code modular and reusable. Learn more about this in the Protocols Guide.
Leverage Swift’s Type Inference
You don’t always need to explicitly declare types. Swift’s type inference handles it for you.
Example:
let earth = "Planet Earth" // Swift knows it's a String
Explicitly stating the type is only necessary when clarity is needed. More on Swift type inference.
Extensions for Organization
Instead of cluttering a class or struct with all functionality, break it up using extensions.
Example:
extension Spaceship {
func startEngine() { print("Engine started.") }
}
This keeps related behavior grouped and makes code more modular. More details can be found in Extensions.
Write Swift-y Code
Avoid writing Swift like Java or C#. For example, don’t use overly verbose getters and setters.
Example of what not to do (Java-style):
class Spaceship {
private var _speed: Int = 0
func getSpeed() -> Int { return _speed }
func setSpeed(speed: Int) { _speed = speed }
}
Do this instead (Swift-style):
class Spaceship {
var speed: Int = 0
}
This leverages Swift’s computed properties instead of unnecessary methods.
Final Thoughts
Following Swift’s best practices isn’t just about writing cleaner code—it’s about making your code more maintainable, scalable, and idiomatic. By sticking to these conventions, you’ll not only improve collaboration with other developers but also make future-you thankful when revisiting your old code. Clean code means fewer headaches, fewer bugs, and better long-term results.
References
Got a Swift convention you live by? Drop it in the comments or shoot me a message. That’s what I’ve got so far, may you have the best composite thrashing code, cheers!