Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
92: ObservableObject and @Published
Up until now, we've mostly relied on @State to keep our views in sync. But as your app grows, you'll realize that putting all your logic inside a View struct is a recipe for a mess. You want your data and your business logic to live in their own dedicated classes. That's where ObservableObject comes in.
Why can't I just use @State inside a class?
It's a common point of confusion. @State is specifically designed for value types (like structs) that are owned by the view. Classes are reference types. When you change a property inside a class, SwiftUI has no way of knowing that the internal data changed because the reference to the class instance itself remains exactly the same.
To fix this, we use the ObservableObject protocol. By conforming your class to this protocol, you're telling SwiftUI: "This object is capable of announcing when its data changes." But just conforming to the protocol isn't enough; you have to tell SwiftUI which specific properties should trigger that announcement.
What is @Published actually doing?
Think of @Published as a megaphone. When you mark a property with @Published, SwiftUI automatically creates a publisher for that variable. Every time that value is set, the object sends out a signal saying, "Hey, I've changed! Anyone watching me needs to redraw themselves."
Let's look at a real example. Imagine we're building a shopping basket. We don't want the UI logic mixed in with the price calculations.
class ShoppingBasket: ObservableObject {
@Published var items: [String] = []
@Published var totalWeight: Double = 0.0
// This is a regular property. Changing this won't trigger a UI refresh.
var internalLogId: String = "LOG_123"
func addItem(_ item: String, weight: Double) {
items.append(item)
totalWeight += weight
}
}
In this snippet, if you add an item, the items array and the totalWeight change. Because they are marked with @Published, any view observing this ShoppingBasket instance will automatically reload. However, if you changed internalLogId, the UI wouldn't budge. This is actually a good thing—you don't want your app re-rendering for data the user can't even see.
How do I actually connect this class to my View?
Creating the ObservableObject is only half the battle. You need to tell the View to listen. You'll typically use @StateObject when you are creating the object for the first time, and @ObservedObject when you're passing an existing object into a subview.
Here is how that looks in practice:
struct BasketView: View {
// We use @StateObject because this view "owns" the basket
@StateObject var basket = ShoppingBasket()
var body: some View {
VStack {
Text("Items in basket: \(basket.items.count)")
Text("Total Weight: \(basket.totalWeight, specifier: "%.2f") kg")
Button("Add Apple") {
basket.addItem("Apple", weight: 0.2)
}
}
}
}
I've seen a lot of developers use @ObservedObject everywhere, but be careful. If the view is destroyed and recreated (which happens often in SwiftUI), an @ObservedObject can be reset. @StateObject ensures the object persists as long as the view exists.
Do I have to mark every single property as @Published?
Definitely not. In fact, you shouldn't. Every time a @Published property changes, SwiftUI evaluates the body of every view watching that object. If you have a massive object with 50 properties and 40 of them are @Published, you might end up with performance stutters because the UI is refreshing way more often than it needs to.
Only mark the properties that directly impact what the user sees on the screen. If it's a helper variable, a cache, or a private ID, keep it as a standard var.
📋 Practical Task
Exercise: Implementing a UserProfile Manager
You need to build a simple user profile system where the UI updates automatically when the user's information changes.
- Create a class named
UserProfilethat conforms toObservableObject. - Add two
@Publishedproperties:username(a String) andfollowerCount(an Int). - Add a regular property called
lastLoginDate(a Date) that is not published. - Create a
ProfileViewthat uses@StateObjectto instantiate theUserProfile. - In the view, display the
usernameandfollowerCountin Text views. - Add a button that increments the
followerCountby 1 each time it's tapped.
Goal: Verify that the follower count updates on the screen immediately when the button is pressed, proving that the ObservableObject is successfully communicating with the view.
There are no comments for now.