Introduction
Swift is a powerful and intuitive programming language for the Apple ecosystem. However, its concise syntax and strong typing often confuse beginners. In this article, we will analyze the most common mistakes made by novice iOS and macOS developers. We will go from the letter A (Automatic Reference Counting) to Z (explicit optional unwrapping). Each point is accompanied by a real code example and an explanation of how to avoid the problem.
1. Mistakes with Optionals (nil and unwrapping)
One of Swift's strongest features is safe handling of nil. But beginners often ignore optionals or use force unwrapping (!) where it is unsafe.
1.1. Force Unwrapping without checking
The most popular mistake is putting an exclamation mark without ensuring the value is not nil.
// ❌ Error: app will crash if user is not foundlet user = users.first!print(user.name)
// ✅ Correct: safe unwrappingif let user = users.first { print(user.name)} else { print("User not found")}1.2. Ignoring guard let
Many beginners use nested if let, creating a "pyramid of doom". Use guard let for early exit from a function.
// ❌ Bad: nested if letfunc process(user: User?) { if let unwrappedUser = user { if let email = unwrappedUser.email { print(email) } }}
// ✅ Good: guard letfunc process(user: User?) { guard let unwrappedUser = user, let email = unwrappedUser.email else { return } print(email)}2. Memory Management (ARC and Strong Reference Cycles)
Swift uses Automatic Reference Counting (ARC), but it is not magic. Beginners often create memory leaks by forgetting about closure capture.
2.1. Strong references in closures
When you pass a closure that captures self, and self holds that closure, a cycle occurs.
class NetworkManager { var onComplete: (() -> Void)? func fetchData() { // ❌ Error: strong reference to self onComplete = { self.updateUI() } // ✅ Correct: weak reference onComplete = { [weak self] in self?.updateUI() } } func updateUI() { /* ... */ }}2.2. Forgetting about unowned
Sometimes you are sure the object will not be nil. Use [unowned self], but only if you guarantee that self will not be deallocated before the closure.
// Dangerous: unowned with asynchronous callsclass ViewController { func loadImage() { ImageService.shared.fetch { [unowned self] image in // If self is deallocated, the app will crash self.imageView.image = image } }}
// Safer: weakfunc loadImage() { ImageService.shared.fetch { [weak self] image in self?.imageView.image = image }}3. Common Syntax Pitfalls
Even simple syntax can be confusing if you don't know the nuances of Swift.
3.1. Confusion between classes and structs
Beginners often make everything a class, forgetting that structs are value types and classes are reference types.
// ❌ Error: modifying a struct's property inside itselfstruct User { var name: String mutating func changeName(to newName: String) { name = newName // Without mutating it won't compile }}
// ✅ For a class, mutating is not requiredclass UserClass { var name: String func changeName(to newName: String) { name = newName }}3.2. Incorrect use of defer
defer