SwiftUI State Management: @State, @Binding, @ObservedObject và khi nào dùng cái nào

SwiftUI State Management: @State, @Binding, @ObservedObject và khi nào dùng cái nào

SwiftUI ra đời mang lại cách tiếp cận declarative hoàn toàn mới cho lập trình iOS. Thay vì ra lệnh cập nhật UI từng bước như UIKit, bạn mô tả “giao diện trông như thế nào khi state là gì” — và framework tự cập nhật khi state thay đổi. Tuy nhiên, chính sự linh hoạt này đặt ra câu hỏi: với bao nhiêu property wrapper khác nhau, bạn biết chọn cái nào cho tình huống nào?

Hiểu rõ SwiftUI state management không chỉ giúp code gọn hơn mà còn tránh bug khó debug do re-render không mong muốn. Bài viết này đi qua từng wrapper phổ biến, đưa ra ví dụ cụ thể và quy tắc chọn nhanh.

Màn hình Xcode hiển thị SwiftUI preview với nhiều state binding
Màn hình Xcode hiển thị SwiftUI preview với nhiều state binding

@State — State nội bộ, thuộc về view

@State dành cho giá trị thuộc sở hữu nội bộ của view. Khi giá trị thay đổi, SwiftUI tự động re-render view. Điểm quan trọng: @State không chia sẻ với bất kỳ view nào khác.

struct CounterView: View {
    @State private var count = 0  // Chỉ dùng trong view này

    var body: some View {
        VStack {
            Text("Count: (count)")
            Button("+1") { count += 1 }
        }
    }
}

Quy tắc vàng: nếu dữ liệu chỉ cần cho một view và không cần truyền ra ngoài, dùng @State. Ví dụ: toggle hiển thị alert, giá trị slider nội bộ, trạng thái expanded/collapsed của một section.

@Binding — Truyền state xuống dưới, hai chiều

Khi bạn cần view con đọc và ghi state của view cha, dùng @Binding. Binding tạo “đường dây hai chiều” giữa hai view.

struct ParentView: View {
    @State private var isOn = false

    var body: some View {
        ToggleView(isOn: $isOn)  // $ tạo binding
    }
}

struct ToggleView: View {
    @Binding var isOn: Bool  // Chỉ nhận, không sở hữu

    var body: some View {
        Toggle("Feature", isOn: $isOn)
    }
}

Áp dụng: form row, custom slider, stepper, hay bất kỳ component nào cần phản ánh state từ cha. Lưu ý: binding không tạo ra sở hữu — nó chỉ là con trỏ tham chiếu.

@ObservedObject — Quan sát object tuân thủ ObservableObject

Khi state phức tạp hơn một giá trị đơn — ví dụ đối tượng network manager, user session, hay cart model — bạn cần @ObservedObject. Object phải tuân thủ protocol ObservableObject và phát hành thông báo qua @Published.

Sơ đồ luồng dữ liệu giữa View, State và ViewModel trong SwiftUI
Sơ đồ luồng dữ liệu giữa View, State và ViewModel trong SwiftUI
class CartModel: ObservableObject {
    @Published var items: [Item] = []
    @Published var total: Decimal = 0
}

struct CartView: View {
    @ObservedObject var cart: CartModel  // Nhận reference

    var body: some View {
        List(cart.items) { item in
            Text(item.name)
        }
    }
}

Khi bất kỳ @Published property nào của CartModel thay đổi, tất cả view đang @ObservedObject nó sẽ re-render. Đây là pattern phổ biến cho view model trong kiến trúc MVVM.

@StateObject — Tạo và sở hữu object trong view

@StateObject ra đời để giải quyết vấn đề “ai tạo, ai sở hữu” với @ObservedObject. Dùng @StateObject khi view tạo ra object lần đầu tiên và giữ quyền sở hữu.

struct ProfileView: View {
    @StateObject private var profile = ProfileModel()  // View sở hữu

    var body: some View {
        // ...
    }
}

Quy tắc thực tế: view tạo view model → dùng @StateObject. View chỉ nhận view model từ cha → dùng @ObservedObject. Nếu dùng nhầm, bạn có thể gặp tình trạng object bị tạo lại nhiều lần, mất dữ liệu tạm.

@EnvironmentObject — Chia sẻ toàn cục trong view hierarchy

Khi nhiều view cùng cần truy cập cùng một đối tượng — theme, authentication session, locale settings — bạn không muốn truyền qua 10 tầng view. @EnvironmentObject giải quyết bằng cách “inject” object vào environment và bất kỳ view nào cũng có thể đọc.

@main
struct MyApp: App {
    @StateObject private var auth = AuthModel()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(auth)  // Inject
        }
    }
}

struct DeepView: View {
    @EnvironmentObject var auth: AuthModel  // Đọc từ environment
    // ...
}

Cảnh báo: nếu không có object tương ứng trong environment, app sẽ crash. Dùng environment cho data thực sự toàn cục, không lạm dụng cho mọi thứ.

Bảng so sánh nhanh

Wrapper Sở hữu Chia sẻ Dùng khi
@State Không State nội bộ view đơn
@Binding Không Hai chiều với cha View con cần đọc/ghi state cha
@ObservedObject Không Nhiều view Nhận object từ cha
@StateObject Nhiều view Tạo và giữ object trong view
@EnvironmentObject Không Toàn hierarchy Data toàn cục như auth, theme

Kết luận

SwiftUI state management không phải là “chọn cái nào mạnh nhất”, mà là “chọn cái nào phù hợp với ownership và scope”. Một view đơn giản chỉ cần @State; form phức tạp cần @Binding; screen có view model cần @StateObject hoặc @ObservedObject; và app-wide setting cần @EnvironmentObject. Áp dụng đúng wrapper giúp code tường minh, dễ test, và tránh lỗi re-render không mong muốn. Để hiểu sâu hơn về kiến trúc tổng thể, bạn có thể tham khảo Hacking with Swift — State System và tài liệu chính thức từ Apple SwiftUI Tutorials.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Drizzle ORM là gì? ORM TypeScript hiệu năng cao thay thế Prisma

Drizzle ORM là gì? Drizzle ORM là thư viện ORM (Object-Relational Mapping) kiểu headless cho TypeScript và JavaScript, tập trung vào hiệu năng cực cao và trọng lượng thấp. Khác…

Xem thêm

Clean Architecture là gì?

Clean Architecture là gì? Clean Architecture là mô hình kiến trúc phần mềm do Robert C. Martin (Uncle Bob) tổng hợp từ các trường phái trước: Hexagonal Architecture của Alistair…

Xem thêm

SQLAlchemy 2.0 là gì? Cải tiến ORM Python hiệu năng 2024

SQLAlchemy 2.0 là gì? SQLAlchemy 2.0 là bản cập nhật lớn của thư viện ORM và SQL toolkit nổi tiếng nhất cho Python, phát hành năm 2023. Nó cung cấp…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất