[Swift]UI상태와 변수를 공유하기 / Sharing UI state and variables

👉🏻 프로젝트 명이 PublisherSubscribe인 경우 입니다.
If the project name is PublisherSubscribe.

👉🏻 SwiftUI에서 @Publisher, @StateObject ,@ObservedObject를 통해서 변수나 상태를 공유합니다.
In SwiftUI, variables and states are shared through @Publisher, @StateObject, and @ObservedObject.

👉🏻SwiftUI에서 뷰의 개념은 struct {} 구조로써 화면에 보여지는 기능을 가지고 있는 것입니다.
In SwiftUI, the concept of a view is a struct {} structure that has the functionality to be displayed on the screen.

✔️@Publisher :
ObservableObject 안에서 사용하는 property wrapper입니다.
View에서 @ObservedObject나 @StateObject로 이 ViewModel을 관찰하면 UI가 자동 갱신됩니다.

This is a property wrapper used within ObservableObject. When the View observes this ViewModel using @ObservedObject or @StateObject, the UI is automatically updated.

✔️ @StateObject :
ViewModel 같은 참조 타입 상태를 View 재생성에도 유지합니다.
Maintains reference type state, such as ViewModel, even when View is recreated.

그러면 View는 언제 재생성 되나?
So when is the View regenerated?

1)State나 Binding이 바뀔 때 / When State or Binding changes

2)부모 View가 업데이트 될 때 / When the parent View is updated

3)화면 rotation, layout change 등이 발생할 때 / When screen rotation, layout change, etc. occur

✔️@ObservedObject :
외부에서 전달받아 관찰만, 재생성 시 객체 소유권 없습니다.
Observe only when received from outside, no ownership of object when recreated.

✔️ObservableObject는 ViewModel이 UI에게 “내가 변했으니 다시 그려달라”라고 알릴 수 있게 하는 SwiftUI 전용 프로토콜입니다.
ObservableObject is a SwiftUI-specific protocol that allows a ViewModel to tell the UI, “I changed, so redraw me.”

👉🏻 이 앱은 버튼을 누를 경우 부모 뷰와 자식 뷰에서 카운터 값이 동일하게 증가하는 앱입니다.
This app is one that increases the counter value equally in both the parent view and child view when a button is pressed.

2.코드/Code

✔️ PublisherSubscribeApp.swift

보통 이 파일은 참조용입니다. 자동생성되기때문에 수정하지 않아도 됩니다.
This file is usually for reference only. It is automatically generated and does not need to be modified.

import SwiftUI

@main
struct PublisherSubscribeApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

✔️ ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            
            ParentView()
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

✔️ ParentView.swift

import SwiftUI

struct ParentView: View {
    
    // @StateObject → ViewModel을 "이 View에서 생성하고 보유"함
    // ViewModel을 직접 사용할 경우 @StateObject사용 함
    
    // @StateObject → "Create and hold the ViewModel in this View"
    // Use @StateObject when using the ViewModel directly.
    
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Parent View Count: \(viewModel.count)")
                .font(.title)
            
            Button("Increase") {
                viewModel.increase()
            }
            
            // 자식에게 ViewModel 전달
            ChildView(viewModel: viewModel)
        }
        .padding()
    }
}

✔️ ChildView.swift

import SwiftUI

// ParentView에 포함되는 뷰
struct ChildView: View {
    // @ObservedObject → 부모에서 전달받은 ViewModel을 "관찰"만 함
    // 자식 뷰가 부모 뷰에게서 ViewModel을 전달 받은경우 @ObservedObject를 사용함.
    @ObservedObject var viewModel: CounterViewModel
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Child View Count: \(viewModel.count)")
                .font(.headline)
            
            Button("Increase from Child") {
                viewModel.increase()
            }
        }
        .padding()
        .border(Color.gray)
    }
}

✔️ CounterViewModel

import Foundation
import Combine

// ObservableObject는 ViewModel이 UI에게 “내가 변했으니 다시 그려달라”라고 알릴 수 있게 하는 SwiftUI 전용 프로토콜입니다.
// SwiftUI에서 ObservableObject는 **“외부에서 관찰 가능한 객체”**를 만들기 위한 프로토콜입니다.

// ObservableObject is a SwiftUI-specific protocol that allows the ViewModel to tell the UI, "I changed, so please redraw me."
// In SwiftUI, ObservableObject is a protocol for creating "objects that are observable from the outside".

class CounterViewModel: ObservableObject {
    
    // @Published: 값이 바뀌면 이벤트를 내보내는 Publisher
    // @Published: Publisher that emits an event when the value changes
    
    @Published var count: Int = 0
    
    func increase() {
        count += 1
    }
}

/*
 
 @Published = objectWillChange.send()를 자동으로 호출해주는 편의 기능입니다.
 원래는 직접 objectWillChange.send()를 호출하면, @Published 없이도 UI를 업데이트 가능합니다.
 단, 개발자가 값이 바뀔 때마다 send()를 직접 호출해야 합니다.
 이런 부분을 자동으로 처리해주는게 @Published입니다.
 
 @Published is a convenience feature that automatically calls objectWillChange.send().
 Originally, if you call objectWillChange.send() directly, you can update the UI without @Published .
 However, developers must manually call send() every time a value changes.
 @Published handles this automatically.
 
 */

✔️ 스크린샷 / ScreenShot

Leave a Reply