-
[Swift] SwiftUI 프로퍼티 래퍼 뿌시기 1: @State와 @Binding테크 2024. 3. 6. 19:25
후... 기본이 탄탄하지 않으니 코드 작성하는게 어렵다 어려워;
@Observable이 뭐지?하고 보다가 그냥 제가 프로퍼티 래퍼를 잘 모르는 것 같아가지구요ㅠㅎ
기초부터 하나씩 하나씩 뜯어보도록 하겠습니다...
일단 프로퍼티 래퍼가 뭐냐?
프로퍼티 래퍼 (Property Wrapper)
- SwiftUI에서 @붙은 애들을 지칭함: @propertyWrapper, @State, @Binding, @Bindable, ...
- 속성(Property, 이하 프로퍼티)과 관련된 행동들을 캡슐화함
- 결과적으로 코드의 가독성을 높임
예를 들어서 아래와 같은 코드가 있다고 생각해 봅시다.
import SwiftUI struct Car { private var _model = "" var model: String { get { self._model.uppercased() } set { self._model = newValue } } init(model: String) { self.model = model } } struct Person { private var _name = "" var name: String { get { self._name.uppercased() } set { self._name = newValue } } init(name: String) { self.name = name } } let car = Car(model: "Benz") let person = Person(name: "kimdora") print(car.model, person.name) // BENZ KIMDORA
뭐 코드가 멀쩡한거 아니냐? 라고 생각할 수 있어요. 네! 멀쩡합니다.
근데 우리가 또 개발잔데 코드 중복은 참을 수가 없죠?
var model: String { get { self._model.uppercased() } set { self._model = newValue } }
var name: String { get { self._name.uppercased() } set { self._name = newValue } }
코드가 중복되어 있으니 중복을 없애고 싶은게 당연한 것 아닙니까!?
이때 등장하는 것이 바로 @propertyWrapper...
import SwiftUI @propertyWrapper struct Uppercase { private var value: String = "" var wrappedValue: String { // 구현 필수 get { self.value.uppercased() } set { self.value = newValue } } }
자, 위 예시처럼 @propertyWrapper를 사용해서 커스텀한 @Uppercase 프로퍼티 래퍼를 만들어주면
아래처럼 간결하게 코드를 작성할 수 있답니다!
struct Car { @Uppercase var model: String init(model: String) { self.model = model } } struct Person { @Uppercase var name: String init(name: String) { self.name = name } } let car = Car(model: "Benz") let person = Person(name: "kimdora") print(car.model, person.name) // BENZ KIMDORA
간단하쥬?
자 고럼 이제 프로퍼티 래퍼가 무엇인지 감이 왔을터이니...
SwiftUI에서 제공하는 여러 프로퍼티 래퍼 중에서
정말 정말 자주 사용하는 2개의 프로퍼티 래퍼를 알아봅시다.
@State
@frozen @propertyWrapper struct State<Value>
- 구조체는 프로퍼티 변경이 불가능하지만 @State를 통해서 프로퍼티 변경 가능
- 보통 App, Scene, View에서 사용 (난 일단 View에서만 사용할거니까 아래에선 View에서 사용한다고 말하겠음^^)
- Private으로 선언하기 때문에 다른 View에서는 접근이 불가능
바로 예시 들어갑니다!
struct PlayButton: View { @State private var isPlaying: Bool = false var body: some View { Button(isPlaying ? "Pause" : "Play") { isPlaying.toggle() } } }
@Binding
@frozen @propertyWrapper @dynamicMemberLookup struct Binding<Value>
아까 @State를 사용하면 다른 View에서 접근하지 못한다고 이야기 했었는데요.
코드를 작성하다보면 View를 대량 생산할 수밖에 없고 View 간 변수 공유가 중요할터인데...
이를 해결하기 위해 @Binding이 등장합니다! (@Bindable 아님 주의)
- 부모 View에서 자식 View로 값을 전달하거나 자식 View에서 부모 View로 값을 다시 전달하는데 사용
- @State 프로퍼티 래퍼로 감싸진 변수 이름 앞에 $가 붙으면 Binding 타입으로 바뀜: projectedValue
예시는 다음과 같습니다.
struct PlayButton: View { @Binding var isPlaying: Bool var body: some View { Button(action: { self.isPlaying.toggle() }) { Image(systemName: isPlaying ? "pause.circle" : "play.circle") } } } struct PlayerView: View { @State private var isPlaying: Bool = false var body: some View { VStack { PlayButton(isPlaying: $isPlaying) } } }
먼저 PlayerView는 PlayButton의 부모 View라고 할 수 있겠네요.
PlyerView에서 @State로 감싸진 isPlaying은 $isPlaying 형태로 PlayButton에 넘겨지게 되는데요.
$isPlaying이 되면 Binding 타입으로 되기 때문에 PlayButton의 isPlaying의 타입과 딱 맞게 되죠?
이제 PlayerView의 isPlaying과 PlayButton의 isPlaying은 서로 연결되었으니 (동체가 되는거임!)
둘 중에 하나라도 값이 바뀌면 다른 View에서의 값도 바뀌게 되는 겁니다!!!
일단 여기까지 포스팅을 마무리하고...
다음 시간에 봅시다...!
Property Wrapper에 대해서 함께 공부하고 싶다면
#PropertyWrapper 태그로 들어가기!'테크' 카테고리의 다른 글
[Swift] SwiftUI 프로퍼티 래퍼 뿌시기 3: @ObservedObject와 @StateObject 차이 이해하기 (0) 2024.03.07 [Swift] SwiftUI 프로퍼티 래퍼 뿌시기 2: @Published 찍먹하기 (feat. ObservableObject) (2) 2024.03.06 [Swift] Foundation에서 제공하는 동기화를 위한 Lock에 대해서 알아보기 | NSLock | NSRecursiveLock | NSConditionLock | NSCondition | NSDistributedLock (1) 2023.10.25 [OS101] 쓰레드 (0) 2023.09.14 [OS101] 프로세스 (1) 2023.09.10