최근 개발을 하다 이슈 사항이 들어왔었습니다. 디버깅 결과 이벤트를 받지 못해 값을 업데이트하지 못하고 있었습니다. 그리고 그 원인은 onChange 메서드였습니다. 그래서 이번에 다시 두 메서드를 공부해볼 예정입니다.
SwiftUI에서 상태 변화와 외부 이벤트를 감지하는 것은 매우 중요합니다. 이 메서드는 각각 상태 변화와 Combine의 Publisher를 통해 이벤트를 감지합니다.
onChange 메서드
onChange는 SwiftUI 내부 상태가 변경될 때마다 지정된 작업을 수행할 수 있는 메서드입니다. 기존에는 뷰가 처음 나타날 때 작업이 실행되도록 설정할 수 있습니다.
of values는 추적할 값을 설정합니다. iOS 17에서 생긴 initial 파라미터는 뷰가 처음 나타날 때에도 action을 실행할지 여부를 설정하며 기본값은 false 입니다. 그리고 action은 값이 변경될 때 실행한 클로저입니다.
struct ContentView: View {
@State private var count: Int = 0
var body: some View {
VStack {
Button {
count += 1
} label: {
Text("Increase Count")
}
}
.padding()
.onChange(of: count, initial: false) { newValue, oldValue in
print("\(newValue)")
}
.onAppear {
print("onAppear---------")
}
}
}
위 예제에서는 count의 변화는 액션에 의해서 값을 감지하고 onChange 메서드가 실행되는 것을 알 수 있습니다.
여기서 initial 부분을 true로 바꾸면 onAppear 메서드가 호출됨과 동시에 onChange 메서드가 호출되어서 함께 찍히는 것을 볼 수 있습니다. 물론 대부분의 앱은 iOS 17보다 낮은 버전이기 때문에 아직은 사용할 수 없는 기능이겠지만 기존의 불편함을 해소하는 방법이 생긴 것 같습니다.
onReceive 메서드
onReceive는 SwiftUI가 Combine의 Publisher에서 전달되 외부 이벤트를 처리할 때 사용됩니다. Publisher로부터 받은 데이터가 변경될 때마다 action 클로저가 실행됩니다. 예를 들어 타이머나 네트워크 이벤트와 같은 외부 이벤트를 처리하기에 적합합니다.
publishe는 데이터를 발생하는 Publisher를 전달합니다. 또한 action은 Publisher가 새로운 데이터를 발행할 때 수행할 작업을 정의하는 클로저입니다.
class ViewModel: ObservableObject {
@Published var count: Int = 0
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
VStack {
Button {
viewModel.count += 1
} label: {
Text("Increase Count")
}
}
.padding()
.onReceive(viewModel.$count, perform: { value in
print("\(value) count")
})
.onAppear {
print("onAppear---------")
}
}
}
onReceive를 사용하면 어떤 액션 없이 “0 count”를 받는 것을 확인할 수 있습니다. 이러한 이유는 Combine의 Publisher가 작동하는 방식과 관련이 있습니다. @Published 속성을 통해 관찰 가능한 값을 변경할 때마다 그 값을 방출하는 Publisher를 자동으로 생성하며, onReceive는 방출되는 이벤트를 감지하는 것입니다. 구독이 시작되면 현재 상태의 값을 즉시 방출하기 때문에 메서드에서 받을 수 있었던 것입니다.
초기 값을 방출하는 이뉴는 현재 상태의 최신 값을 구독자가 항상 알 수 있도록 보장하기 위한 것입니다. 초기 상태 방출이 없으면 구독자가 구독한 시점 이후에 발생하는 업데이트만 받을 수 있어, 구독 이전의 상태 정보를 잃을 수 있습니다. 때문에 최신 상태를 즉시 알리는데 유리하며, Combine의 철학에 따라 상태와 데이터 흐름을 일관성 있게 유지해줍니다.