4T2F / ThinkBig

🌟씽크빅 스터디🌟
5 stars 1 forks source link

SwiftUI 자주 사용되는 Property Wrapper 정리 #2

Open kmh5038 opened 10 months ago

kmh5038 commented 10 months ago

SwiftUI 자주 사용되는 Property Wrapper 정리

kmh5038 commented 10 months ago

ProPerty Wrapper?

코딩에서 항상 중요시 여겨지는것중 클린코드가 있습니다.

클린코드에서 가장 기본이 되고 중요한 것은 중복된 코드를 제거하는것이다.

그 방법 중 가장 쉬운 방법은 메서드로 공통된 로직을 묶어서, 호출만 다른 시점에서 해주는것이다.

이런 방식을 Property에서는 수행 할 수 있도록 하는것이 Property wrapper이다.

Property Wrapper는 Swift 5.1에서 추가되었고, 특별한 기능을 갖는 속성을 정의하는데 사용된다.

그 기능을 갖게 함으로써 코드의 가독성을 높여준다. (쉽게 말해 변수의 기능을 확장해준다)

사용 전(예시)

struct Recteangle {
  private var width: Int
  private var height: Int

  init(width: Int, height: Int) {
     self.width = width
         self.height = height
    }

    var limitedWidth: Int {
        get { return min(width, 10) }
        set { width = newValue }
    var limitedHeigth: Int {
        get { return min(height, 10) }
        set { height = newValue }
    }

사용 후(예시)

@propertyWrapper struct LimitedValue {
        pirvate var value: Int = 0

        var wrappedValue: Int {
            get { self.value }
            set { value = min(10, newValue) }
    }
}

struct Rectangle {
    @LimitedValue var width: Int
    @LimitedValue var height: Int
}

사용 전의 예시는 다른 변수를 생성할때마다 같은 코드를 반복해줘야한다. 그 과정 자체가 매우 비효율적이고 가독성이 떨어진다.

반면 사용 후의 예시는 다른 변수를 생성할때 @LimitedValue 붙여주고 변수를 생성하면 같은 속성을 가진 변수로 생성된다.

SwiftUI에서 제공하는 Property Wrapper

@ State

- 기능 - struct 내의 프로퍼티를 수정할 수 있게 만듭니다. - @ State로 선언된 변수를 변경했을 때, 이 변수를 가져다 쓴 뷰를 다시 그립니다. - 요약 - @ State 변수는 뷰에서 사용됩니다. - 어딘가에서 무언가에 의해 @ State 변수가 변경, 수정됩니다. - @ State 변수가 변경되면 이 변수를 가져다 쓴 뷰는 다시 그려집니다. - State로 선언한 Property는 내부에서만 사용하기 때문에 private을 선언하고 사용하자.
## 예시 ![스크린샷 2023-12-21 오후 4 20 29](https://github.com/4T2F/ThinkBig/assets/144766297/3cdd3b59-85f6-48db-b9af-a2371460b34e)
@ Binding

- 기능 - 부모 뷰에서 자식 뷰로 값을 전달하고, 자식 뷰에서 부모 뷰로 값을 다시 전달하는 데 사용됩니다. 부모 뷰의 속성을 자식 뷰에서 변경하는 데 사용된다. (양방향) - 요약 - @ State변수를 다른 뷰에서도 가져다 쓸 수 있다. 하지만 다른 뷰에서 변수를 수정할 수 없다 - @ Binding을 사용하면 다른뷰에서도 State변수를 수정할 수 있다. -넘겨줄때는 $기호를, 넘겨받는 곳에서는 @ binding을 붙여준다.
## 예시 1 ![스크린샷 2023-12-21 오후 5 57 28](https://github.com/4T2F/ThinkBig/assets/144766297/aa6601a4-bbc0-43b3-8012-de087ceeca55) 이 경우 SubView에 기존에 @ State 변수를 전달한게 아니라 새로운 @ State 변수가 생성된 것이다. 그래서 버튼을 눌러도 SubView의 숫자는 오르지않는다. ## 예시 2 ![스크린샷 2023-12-21 오후 9 27 46](https://github.com/4T2F/ThinkBig/assets/144766297/7d909ee5-7416-4354-831a-9fd295e2178d) 이 예시는 `SubView(number: number)로 SubView의 변수로 전달`을 했기 때문에 버튼을 눌렀을때 `두 숫자 모두 증가한다.` ![스크린샷 2023-12-21 오후 9 28 47](https://github.com/4T2F/ThinkBig/assets/144766297/0faa59d7-55c1-4c99-a1f2-a41f39a6858b) 하지만 SubView도 값을 증가시키는 버튼을 만들어 사용하려하면, `값을 변경할 수 없다는 에러`가 발생한다. @ State 사용 시 @ State가 `선언된 곳에서는 값을 변경할 수 있지만`, `다른 뷰에서 호출된 경우에는 값 변경이` `불가능하다`. 이럴때 필요한것이 @ Binding이다. ## 예시 3 ![스크린샷 2023-12-21 오후 5 57 05](https://github.com/4T2F/ThinkBig/assets/144766297/dee0903e-f806-481e-8ae8-618e0eb1d7a8) 전달받은 곳에서 `수정하고 싶다면 Binding`으로 변환해서 전달해주기. `넘겨주는 곳에서 $`, `받는곳에서 @ Binding`으로 기억하면 편하다. ## $(Projected Value) Property Wrapper의 기능 중 하나이다. $ 기호를 붙이면 Property Wrapper에 저장된 값을 Projected Value로 변환시켜준다. 그러면 이 변수에 저장되어 있는 값을 `지정된 타입으로 변경시키고 반환해준다`. @ State에서 이 기능을 사용해서 $을 붙여서 @ Binding으로 변환시켜서 값을 반환시켜 줄 수 있던거다. ($을 붙이면 타입에 맞게 알아서 변환시켜줌)
@ ObservableObject, @ Published, @ ObservedObject

- 기능 - ObservableObject를 준수한 Class는 objectWillChange라는 property를 사용할 수 있다. objectWillChange는 objectWillChange.send()라는 함수를 사용할 수 있는데 변경된 사항이 있다고 알려주는 함수이다. 이를 대신해주는 기능이 @ Published가 해주는 역할이다. 해당 변수가 변경되면 자동으로 objectWillChange.send()를 호출해준다. - @ Published로 표시된 속성이 있는 오브젝트가 변경될 때마다 해당 오브젝트를 사용하는 모든 View가 다시 로드되어 해당 변경 사항을 반영합니다. - 요약 - ObservableObject를 사용 할 때 에는 ObservableObject protocol,@Published, @ObservedObject 3가지를 모두 구현해야한다. - ObservableObject는 Combine에 속한 기능이고 Class에서만 사용 가능한 Protocol이다. - @Published는 값이 변경하면 변경사항이 있다고 알려주고, @ObservedObject는 변경 사항을 감지하고 해당 변경 사항이 뷰에 즉시 반영되도록 해주는 역할을 한다.
## 예시 ![스크린샷 2023-12-24 오후 1 40 39](https://github.com/4T2F/ThinkBig/assets/144766297/c30c7e63-a84d-473e-9559-ea32e9081a02) ObservableObject 프로토콜을 따르는 클래스 TimerData를 만든다. 1초씩 증가시킬 변수 timeCount의 변경 사항을
감지해야하기 때문에 @ Published로 지정한다. ![스크린샷 2023-12-24 오후 1 40 58](https://github.com/4T2F/ThinkBig/assets/144766297/c1fb1a2e-7277-4ac8-9f60-32b5eba7d84b) ContentView SecondView 둘 다 timerData를 @ ObservedObject로 지정했기 때문에 변경 사항을 감지해서 뷰에 반영해주기 때문에 1초 시간이 지날때마다 Text에 숫자가 증가한다.(2개의 뷰 모두 같은 시간이 흐른다.) reset을 해도 두 뷰 모두 반영되어 0초가 되어있다. ![스크린샷 2023-12-24 오후 1 41 11](https://github.com/4T2F/ThinkBig/assets/144766297/3366d25b-fd58-42ce-8400-459103c502d5) ![스크린샷 2023-12-24 오후 1 41 15](https://github.com/4T2F/ThinkBig/assets/144766297/459f9ae7-8340-48c6-9895-2959d1009536)
@ EnviromentObject

- 기능 - 앱의 데이터 모델을 여러 뷰간에 공유할 때 쓰인다. - ObservedObject처럼 ObservableObject프로토콜을 준수하는 클레스의 인스턴스를 참조한다. - 최상위 뷰에서 생성하고 **`environmentObject(_:)`** 함수를 통해 주입하면, 해당 객체는 모든 하위 뷰에서 직접 참조하여 사용할 수 있게 됩니다. - 요약 - 여러 뷰 간에 데이터를 공유하고 싶을때 사용한다. - 환경에 공유된 데이터를 전역적으로 사용할 수 있기 때문에 뷰가 전달할 필요 없이 모든 뷰에서 접근가능. - 전역 데이터가 변경될 때마다 해당 객체를 사용하는 모든 뷰를 다시 그린다.
## 예시 ![스크린샷 2023-12-24 오후 2 27 12](https://github.com/4T2F/ThinkBig/assets/144766297/d85dbdd7-b222-4416-912f-eca74dcc4ab1) ObservalbeObject 프로토콜을 준수하는 클레스를 만든다. ![스크린샷 2023-12-24 오후 2 27 27](https://github.com/4T2F/ThinkBig/assets/144766297/8e4e1c9e-f11b-4463-b11b-9af76e688a35) 최상위 뷰에서 EnviromentObject로 사용할 UserData 인스턴스 생성하고 주입해준다. ![스크린샷 2023-12-24 오후 2 27 12](https://github.com/4T2F/ThinkBig/assets/144766297/25b93377-f3f1-4b57-8acb-0462670a7403) 최상위뷰에 주입된 데이터를 사용할 각각의 뷰에서 직접 참조해여 사용할 수 있다. ![스크린샷 2023-12-24 오후 2 27 56](https://github.com/4T2F/ThinkBig/assets/144766297/0df187b5-8a2f-4125-b95d-4440dcfe02a3) ![스크린샷 2023-12-24 오후 2 28 39](https://github.com/4T2F/ThinkBig/assets/144766297/caac7acb-a13b-4222-9817-cc7eeb19b752)
@ Enviroment

- 기능 - 환경 설정과 값을 읽거나 설정할때 뷰에 전달할 때 사용한다. - 시스템이나 앱의 전역 설정을 포함하여 뷰에 영향을 주는 환경 값에 엑세스하는데 사용한다. - 요약 - 사용하려는 공유 데이터의 이름을 SwiftUI에서 제공하는 keyPath를 전달하여 사용한다. - 뷰가 생성되는 시점에 값이 자동으로 초기화된다.
## 예시 ![스크린샷 2023-12-24 오후 2 46 55](https://github.com/4T2F/ThinkBig/assets/144766297/494c6849-f30e-48c8-8823-f7b5bfcde939) 환경 변수로 colorScheme값을 사용하여 현재 화면모드에 따라 Text를 출력해준다. SwiftUI에서 제공해주는 colorScheme값을 사용하여 화면모드를 간단하게 구별할 수 있다. ![스크린샷 2023-12-24 오후 2 47 04](https://github.com/4T2F/ThinkBig/assets/144766297/f00fa3be-3c42-4085-8e3a-4aa3db93716c) 다크모드
ha-nabi commented 10 months ago

@Enviroment 에는 여러가지 환경 변수가 있는걸로 알고 있는데 조금만 알려주시면 감사하겠습니다 ㅎㅎ

Hminchae commented 10 months ago

사용시에 단점이 있다면 어떤게 있을까요? 예를 들어 @EnvironmentObject는 남용 시 데이터 의존성, 복잡성 문제를 야기해서 정말 필요할 때 써야한다고 알고있습니다. 어떨때 사용하면 적합할지 알고싶습니다.