소피it블로그

[SwiftUI] State 정리 본문

개발_iOS/스위프트UI

[SwiftUI] State 정리

sophie_l 2022. 8. 11. 13:05

https://developer.apple.com/documentation/swiftui/state

 

Apple Developer Documentation

 

developer.apple.com

스위프트UI에 의해 관리되는 프라퍼티 래퍼 타입으로, 값을 읽고 쓸 수 있음

 

스위프트UI는 state로 선언한 프라퍼티의 저장을 관리한다. 해당 값이 변경되면 스위프트UI는 그 값에 의존하는 뷰 위계의 일부분을 갱신한다. 뷰 위계질서 내부에 저장된 한 값에 대한 유일한 source of truth로써 state를 사용하라.

 

State 인스턴스는 그 자체로서 값은 아니다. 이는 값을 읽고 쓰는 수단에 해당한다. state의 값에 접근하기 위해서는 프라퍼티명을 부르면 되는데, 그 결과로써 wrappedValue 프라퍼티 값이 반환된다. 예를 들면 PlayButton뷰 내부에서 isPlaying state 프라퍼티를 읽고 업데이트 하려면 해당 프라퍼티를 직접적으로 부르면 된다.

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

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

자식 뷰에게 state 프라퍼티를 전달할 경우 스위프트UI는 부모 뷰에서 값이 변할 때마다 자식 뷰를 업데이트하지만, 자식 뷰는 값을 변경할 수 없다. 자식 뷰가 저장된 값을 변경할 수 있도록 하기 위해서는 Binding을 사용하여 전달하라. state의 projectedValue에 접근함으로써 state 값에 바인딩을 가져올 수 있는데, 이는 프라퍼티명 앞에 달러 기호($)를 적어줌으로써 가능하다.

예를 들어, 위의 예시에서 플레이 버튼에서 isPlaying state를 제거하는 대신 버튼이 state에 바인딩을 취할 수 있게 해줄 수 있다.

// 자식 뷰

struct PlayButton: View {
    @Binding var isPlaying: Bool

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

그런 후에 state를 선언하고 state에 대하여 바인딩을 생성하는 player 뷰를 달러 기호를 사용하여 정의해줄 수 있다.

// 부모 뷰

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) // Pass a binding.
        }
    }
}

뷰 위계질서 내에서 특정 뷰를 인스턴스화하는 상황에서는 그 뷰의 state 프라퍼티를 이니셜라이징 하지 마라. 이는 스위프트UI가 제공하는 저장 관리와 충돌이 일어날 수 있기 때문이다. 이 충돌을 피하기 위해서는 state를 항상 private로 선언하고, 뷰 위계질서 내에서 그 값에 접근해야하는 뷰들 중 가장 상위의 뷰에 위치시키도록 하라. 그런 후 그 state에 접근해야 하는 자식 뷰들과 공유하라. 공유는 읽기 전용 접근을 위해 직접적으로 할 수도 있고, 읽고 쓰기 접근을 위한 바인딩으로 공유할 수도 있다.

state 프라퍼티는 모든 스레드에서 안전하게 mutate할 수 있다.