티스토리 뷰

안녕하세요.

오늘은 SwiftUI를 접하면서 동시에 Property Wrapper 또한 접하게 되면서 주로 사용하는 것들에 대해서 정리할 필요성을 느껴 Property Wrapper에 대해 작성해 보려 합니다.

때문에 이번에 알아볼 Property Wrapper를 소개하면,

  • @State
  • @Binding
  • @Published
  • @ObservedObject
  • @StateObject
  • @Environment

위 6가지에 대해 알아볼까 합니다.

6가지 외에도 더 많기도 하고, 직접 커스텀 해서 만들 수도 있지만, 프로젝트하면서 가장 많이 사용했던 것들만 뽑아봤습니다.

사실 6개도 많아서 2편으로 나눠 작성해보려 합니다.

먼저, 프로젝트 하면서 가장 많이 사용되었던 State 부터 알아보겠습니다.

@State

A property wrapper type that can read and write a value managed by SwiftUI.
출처: 공식문서

State Property Wrapper는

쉽게 접근하면, 변경될 수 있는 View의 상태 값입니다.

View는 State 값을 관찰하고 있다가 State 값이 변경되면 그에 맞게 View 계층 구조의 일부를 업데이트하게 됩니다.

@State private var text: String = ""

위와 같이 변수를 선언할 수 있습니다.

또한, private로 선언하여 외부에서 State 변수에 접근하지 못하게 해야 모든 스레드에서  State 변수의 값을 안전하게 변경할 수 있습니다.

추가로, TextField와 같이 하위 View가 Binding 타입의 파라미터를 가지고 있다면,

TextField("", text: $text)

변수 앞에 "$"를 이용하여 변수를 참조할 수 있도록 주소값을 전달합니다.

이는 C언어의 포인트와 유사한 형태를 가졌습니다.

@Binding

A property wrapper type that can read and write a value owned by a source of truth.
출처: 공식문서

Binding Property Wrapper는 

State 값을 

struct ContentView: View {
    @State private var text: String = ""
    var body: some View {
        VStack {
            Text(text)
        }
        .padding()
    }
}

위처럼 View 내부에서 선언하는 것이 아닌 상위 View로부터 전달받는 경우 즉, 이니셜라이즈를 통해 State 값을 상위 View로부터 주입받는 경우 State가 아닌 Binding을 써주면 됩니다.

struct ContentView: View {
    @State private var text: String = ""
    var body: some View {
        VStack {
            Text(text)
            TextView(text: $text)
        }
        .padding()
    }
}

struct TextView: View {
    @Binding var text: String
    var body: some View {
        TextField("", text: $text)
            .textFieldStyle(.roundedBorder)
    }
}

위와 같이 예제 코드를 작성해 보았습니다.

TextView는 상위 View인 ContentView로부터 상태 값을 전달받고 있습니다.

위와 같은 상황인 경우 State가 아닌 Binding Property Wrapper를 사용해 주면 되겠습니다.

추가로, Binding은 주입받는 상태 값이기 때문에 초기값을 가질 수 없으며, private로 선언할 수도 없습니다.

@Published

A type that publishes a property marked with an attribute.
출처: 공식문서

Published는 sink를 통해 구독할 수 있고, Class 내부에서만 초기화할 수 있습니다.

class PressedObserver {
    @Published var isTap: Bool = false
}

위와 같이 Class 내부에서 초기화가 가능하며, 

private func bind() {
    pressedObserver.$isTap.sink {
        self.text = $0 ? "Button pressed" : "Button not pressed"
    }
    .store(in: &anyCancellable)
}

위처럼 sink를 통해 구독하여, 반응형으로 구현할 수 있습니다.

struct ContentView: View {
    @State private var text: String = ""
    @State private var anyCancellable = Set<AnyCancellable>()
    private var pressedObserver = PressedObserver()
    var body: some View {
        VStack {
            Text(text)
            Button("버튼") {
                pressedObserver.isPressed.toggle()
            }
        }
        .padding()
        .onAppear {
            bind()
        }
    }
    private func bind() {
        pressedObserver.$isPressed.sink {
            self.text = $0 ? "Button pressed" : "Button not pressed"
        }
        .store(in: &anyCancellable)
    }
}

class PressedObserver {
    @Published var isPressed: Bool = false
}

위와 같이 예제 코드를 작성해 보았습니다.

버튼을 클릭하면, isPressed 값이 변경되고, 이를 sink를 통해 구독하여 text 값을 변경했습니다.

자 오늘은 @State, @Binding, @Published에 대해 정리해 보았습니다. 다음 글에서 나머지 @ObservedObject, @StateObject, @Environment에 대해 정리해 보겠습니다.

👋👋👋👋

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함