이모저모/Swift

Observable과 ViewModel State에 대한 고찰

ARpple 2025. 3. 26. 00:57
대부분 iOS ViewModel에서 상태를 관리하기 내부에 쓰이는 값들을 하나의 구조체로 그 객체가 갖는 상태를 선언해서 관리한다.
@Observable class ViewModel{
    var state:State
    struct State{
        var isFirstAppear = true
        var isTipAlertPresented = false
    }
    ...
}
자세한 Observable 동작은 공식문서에...
 

Migrating from the Observable Object protocol to the Observable macro | Apple Developer Documentation

Update your existing app to leverage the benefits of Observation in Swift.

developer.apple.com

 

 

실험 가설

  1. 2개의 State 내부 변수를 둔다.
  2. 두 변수 각각 withObservationTracking 검사하는 메서드를 만든다.
  3. 한 변수만 변화하고 두 메서드 모두 실행한다.
  4. 두 메서드의 withObservationTracking 모두 onChange 핸들러에 print 문이 출력되면 Observable의 고유한 프로퍼티 접근에 위배된다.

ViewModel 코드

@Observable
public final class ViewModel {

    public var state: State
    public init() {
        self.state = State()
    }

    @MainActor
    public struct State: Equatable {
        public var isFirstAppear = true
        public var isTipAlertPresented = false
    }
    public func renderIsFirstAppear() {
        withObservationTracking {
            print("renderIsFirstAppear: ",self.state.isFirstAppear)
        } onChange: {
            print("Schedule renderer. renderIsFirstAppear")
        }
    }
    public func renderIsTipAlertPresented() {
        withObservationTracking {
            print("renderIsTipAlertPresented: ",self.state.isTipAlertPresented)
        } onChange: {
            print("Schedule renderer. renderIsTipAlertPresented")
        }
    }
}

Timer 실행 코드

Task {
    let exploreVM = ViewModel()
    Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
        exploreVM.state.isFirstAppear.toggle()
        exploreVM.renderIsFirstAppear()
        exploreVM.renderIsTipAlertPresented()
    }
    exploreVM.renderIsFirstAppear()
    exploreVM.renderIsTipAlertPresented()
}

실행 결과

두 render 함수 모두 호출된다..!

해결법 - State에 Observable을 걸어준다면?

State에 Observable을 주고 class로 바꿈

import Observation

@Observable
public final class ExploreViewModelObservable {
    ...
    @Observable
    public class State {
        public var isFirstAppear = true
        public var isTipAlertPresented = false
    }
}

실행 결과

값을 바꾼 renderIsFirstAppear의 render만 호출된다

If) State에 Observable을 안 걸어주고 class로만 한다면?

@Observable
public final class ExploreViewModelObservable {
    ...
    public class State {
        public var isFirstAppear = true
        public var isTipAlertPresented = false
    }
}

실행 결과

두 랜더링 모두 호출이 안된다..! Observer가 없다는 뜻..!

 

ps. 사실 웬만하면 크게 렌더링 성능이 떨어질까 싶긴한데...

테스트 파일

ObservationTry.playground.zip
0.02MB