iOS/Swift

[Swift, SwiftUI] Property Wrapper (2) @State/@Binding, @Published/@ObservedObjec

SwiftyCody 2022. 8. 31. 05:22

지난 글에서는 Property Wrapper가 무엇인지 정리해봤습니다.
이번 글에서는 Swift, SwiftUI로 작성 시 자주 쓰게 되는 Property Wrapper를 정리해보겠습니다.

 

@State와 @Binding

@State@Binding은 SwiftUI에서 자주 사용하는 Property Wrapper입니다.
@State로 선언한 값을 참조하여 SwiftUI를 그리면, 해당 값이 변경될 때 SwiftUI가 이를 반영하여 변경됩니다.

// 예시
struct PlayButton: View {
    @State private var isPlaying: Bool = false

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

 

@State로 선언한 값은 선언된 SwiftUI Struct내에서 참조하고 변경이 가능하며, 자식 뷰에서는 참조만 가능하고 값을 변경할 수는 없습니다.
자식 뷰에서 값을 참조하고 값을 변경도 하고 싶다면, 자식 뷰에서는@Binding을 사용하면 됩니다.

struct PlayButton: View {
    @Binding var isPlaying: Bool // Binding으로 변경
    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

 

그리고 부모 뷰의@State값을 자식 뷰의@Binding값으로 전달할 땐 $기호를 앞에 붙여줍니다.

struct PlayerView: View {
    var episode: Episode
    @State private var isPlaying: Bool = false

    var body: some View {
        VStack {
            Text(episode.title)
                .foregroundStyle(isPlaying ? .primary : .secondary)
            PlayButton(isPlaying: $isPlaying) // Binding에게 참조를 전달
        }
    }
}

 

@State를 뷰계층에서 초기화 시 SwiftUI의 Storage관리와 충돌이 일어날 수 있기 때문에
이를 방지하기 위해서 private로 선언해서 사용하기를 권장합니다.
@State@Binding은 모든 스레드에서 안전하게 값 변경이 가능합니다.

@Published, 그리고 @ObservedObject

위에서 본 @State@BindingSwiftUI 내에서 뷰의 상태값을 변경하는데만 사용하도록 권장되고 있습니다.
만약 뷰 외부에서도 사용하고 싶다면? Model 클래스에서 값을 바인딩하고 변경된 값을 반영하고 싶다면?
@Published, @ObservedObject를 사용하면 됩니다.

먼저 @Published를 먼저 보겠습니다.
@PublishedCombinePublished를 Property Wrapper로 감싼 것으로
ObservableObject 프로토콜을 따르는 개체 내에서 사용되고,
@Published 프로퍼티의 값이 변경되었을 때, 해당 값을 방출합니다.
정확히는 '값이 변경되기 전에(objectWillChange)' 값을 내보냅니다.
@Publishedclass의 프로퍼티에만 사용할 수 있습니다.
아래는 ObservableObject 프로토콜로 작성된 CounterViewModel 내에서 @Published로 작성된 count 프로퍼티입니다.

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
}

 


@Published로 선언된 값을 SwiftUI에서 받아서 사용할 때
@ObservedObject Property Wrapper가 사용됩니다.
아래와 같이 CounterViewModel의 count값을 바꿔주면, 변경된 count값이 방출되어 Text에 반영이 됩니다.

struct Counter: View {
    @ObservedObject var viewModel = CounterViewModel()
    
    var body: some View {
        HStack(spacing: 20) {
            Button {
                viewModel.count = viewModel.count-1 >= 0 ? viewModel.count-1 : 0
            } label: {
                Image(systemName: "minus.square")
                    .font(.system(size: 50))
            }
            
            Text("\(viewModel.count)")
                .font(.system(size: 50))
            
            Button {
                viewModel.count += 1
            } label: {
                Image(systemName: "plus.square")
                    .font(.system(size: 50))
            }
        }
    }
}

다음 글은 이어서,
Property Wrapper 중 @EnvironmentObject@Environment에 대해서 정리할 예정입니다.