[SwiftUI] Property Wrapper

2dubu·2022년 8월 11일
1

SwiftUI

목록 보기
2/2
post-thumbnail

SwiftUI를 공부하시거나 관련 강의를 듣다 보면 @State, @Binding, @ObservableObject, @EnvironmentObject 등등 @__ 형태의 코드를 많이 볼 수 있습니다.

이런 형태의 문법을 Property Wrapper라고 합니다. Property Wrapper는 Swift 5.1 에서 추가된 기능이며, 해당 글에서는 SwiftUI에서 주로 사용되는 Property Wrapper에 대해 정리해보려 합니다. 😊

@State

@frozen @propertyWrapper struct State<Value>

@State는 변화가 생기면 해당 변수의 값을 읽거나 새로 쓸 수 있음을 의미하는 property wrapper입니다.

UIKit에서는 어떠한 변수에 변화가 생기면 해당 변화를 직접 관찰하고 반영해 주어야 하지만, SwiftUI 에서는 property wrapper 를 활용해 이러한 작업을 자동화할 수 있습니다.

즉, @State 값이 변경되면 뷰는 해당 value 의 appearance를 무효화 하고 다시 body 값을 계산하게 되어 뷰가 자동으로 업데이트 됩니다.

@State는 단어 그대로 현재 상태를 나타내는 속성으로써 주로 뷰의 어떤 값을 저장하는 데 사용합니다. 그리고 현재 뷰 UI의 특정 상태를 저장하기 위해 만들어진 것이기 때문에 보통 Private로 지정하여 사용합니다.


@Binding

@frozen @propertyWrapper @dynamicMemberLookup struct Binding<Value>

위의 @State 값을 다른 뷰에서 사용할 때 @Binding을 사용합니다. 사용시에는 앞에 $를 사용해 Bining 변수임을 나타냅니다. @State 로 선언된 속성에 변경이 생기면 @Binding 변수에서 이를 인지하고 해당 값에 따른 뷰 변화를 바로 반영할 수 있도록 하는 방식입니다.

값을 외부에서 사용하기 때문에 보통 Private를 사용하지 않습니다.


@ObservableObject

@propertyWrapper @frozen struct ObservedObject<ObjectType> where ObjectType : ObservableObject

클래스 프로토콜 ObservableObject를 클래스에 채택하면 해당 클래스의 인스턴스를 관찰하고 있다가 값이 변경되면 뷰를 업데이트 합니다. @State는 변수와 같은 작은 값을 관찰하며 뷰를 업데이트 했다면, @ObservableObject는 클래스 형태의 조금 더 복잡한 형태의 값을 관찰할 때 사용됩니다.

String이나 integer같은 간단한 로컬 프로퍼티대신 외부 참조 타입(external reference type)을 사용한다는 점을 제외하면 @State와 매우 유사합니다.

ObservableObject를 사용할 때에는 다음 2가지를 더 구현하여야 합니다! 🤯

@Published

ObservableObject에서 속성을 선언할 때 사용하는 PropertyWrapper입니다. @Published로 선언된 속성이 ObservableObject에 포함되어 있다면 해당 속성이 업데이트 될 때마다 뷰를 업데이트합니다.

@ObservedObject

앞서 설명한 ObservableObject를 구독하고 값이 업데이트 될 때 마다 뷰를 갱신하는 PropertyWrapper입니다.

class UserInfo: ObservableObject {
	@Published var name: String
    @Published var age: Int
    var email: String
}

struct ContentView: View {
    @ObservedObject var userInfo = UserInfo()
    
    var body: some View {
        ...
    }
}

즉, 해당 코드에서는 ContentView의 userInfo 객체의 name과 age값이 변경되었을 때 뷰가 업데이트 되는 것이죠!


@EnvironmentObject

@frozen @propertyWrapper struct EnvironmentObject<ObjectType> where ObjectType : ObservableObject

@EnvironmentObject는 쉽게 생각하면 싱글톤과 비슷한 개념으로 이해해볼 수 있습니다. 특정 데이터를 여기저기 많은 뷰에서 사용할 때 해당 데이터를 @EnvironmentObject로 선언해 구현할 수 있습니다. 모든 view가 읽을 수 있는 shared data라고 표현할 수도 있을 것 같습니다. 🥸

물론 같은 기능을 @ObservedObject로도 구현할 수 있지만, @EnvironmentObject로 선언하면 앱의 어느곳에서나 공유할 수 있는 데이터가 된다는 것이죠! 모델이 변경되면 이 EnvironmentObject가 바인딩되고 있는 모든 곳은 view가 자동으로 업데이트 되는 것이 큰 장점입니다.

다른 점이라 한다면 반드시 environmentObject (_ :) 메소드를 호출하여 상위 뷰에서 모델 객체를 설정해야합니다.

// file: SceneDelegate.swift

/// ContentView에 UserInfo 객체를 environmentObject로 설정
let contentView = ContentView().environmentObject(UserInfo())

if let windowScene = scene as? UIWindowScene {
	let window = UIWindow(windowScene: windowScene)
	window.rootViewController = UIHostingController(rootView: contentView)
	self.window = window
	window.makeKeyAndVisible()
}
// file: ContentView.swift

class UserInfo: ObservableObject {
	@Published var name: String
    @Published var age: Int
    var email: String
}

struct ContentView: View {
    @EnvironmentObject var userInfo: UserInfo
    
    var body: some View {
        ...
    }
}

위 코드처럼 구현할 수 있으며, 이때 UserInfo를 EnvironmentObject로 바인딩하고 있는 모든 뷰는 데이터가 변경되었을 때 자동으로 업데이트 됩니다. 🥳


참고한 글

https://value-of-life.tistory.com/160
https://velog.io/@nnnyeong/iOS-SwiftUI-State-Binding
https://zeddios.tistory.com/964

글 읽어주셔서 감사합니다. 😊
질문과 지적은 편하게 남겨주세요!

profile
iOS Developer 👶🏻

0개의 댓글