apple/SwiftUI & Combine

[SwiftUI] EqutableView (feat. POD)

lgvv 2023. 8. 8. 21:59

- EqutableView란?
> EqutableView는 이전 값과 비교하여 새 값이 이전 값과 동일한 경우 자식 업데이트를 방지하도록 설계된 뷰입니다.

 

`iOS 13.0+` `iPadOS 13.0+` `tvOS 13.0` `watchOS 6.0+` `visionOS 1.0+ Beta

 

[apple-docs-EqutableView](https://developer.apple.com/documentation/swiftui/equatableview)

 

💡 들어가기 전.


- 아래의 개념에 대한 지식이 반드시 필요함. 필요한 이유는 글 제일 아래에서 설명
   - [Plain ol' Data](https://rldd.tistory.com/551)

 

 

EqutableView Usage


1. 내가 EqutableView로 만들고자 하는 곳에 Equtable을 채택

struct LuckyBoxStateView: View, Equtable { ... }

 

2. 사용하는 곳에서

Equtable(content: LuckyBoxStateView(text: text))

or

/// ✅ 요 방식이 선언적이라 더 나음
LuckyBoxStateView(text: text)
    .equtable()

 

예제 시나리오


- 내가 만드는 서비스에 확률형 아이템인 럭키박스가 있다고 가정.
- 럭키박스를 구매시 특정 확률에 따라 배수가 결정.
- 럭키박스 오픈시 결정된 배수가 UI에 나타나는 방식.

 

 

✅ Code Smaple

import SwiftUI

enum LuckyBoxState: String {
    case state1 = "x10배"
    case state2 = "x2배"
    case state3 = "x0.5배"
    case state4 = "x0.1배"
    
    static func result(number: Double) -> String {
        if number < 5 {
            return LuckyBoxState.state1.rawValue
        } else if number < 15 {
            return LuckyBoxState.state2.rawValue
        } else if number < 45 {
            return LuckyBoxState.state3.rawValue
        } else {
            return LuckyBoxState.state4.rawValue
        }
    }
}

struct ContentView: View {
    @State private var number: Double = 0
    @State private var count: UInt = 0
    
    private let size: CGFloat = 200
    
    var body: some View {
        print("count: \(count) | number: \(number)")
        
        return VStack {
            LuckyBoxStateView(text: LuckyBoxState.result(number: number))
                .equatable()
                .frame(width: size, height: size * 2 / 3, alignment: .center)
                .background(.blue.opacity(0.5))
                .cornerRadius(10)
            
            Button {
                number = Double.random(in: 0..<100)
                count += 1
            } label: {
                Text("럭키박스 오픈")
            }

            Text("당첨번호: \(number)")
            Text("오픈차수: \(count)")
        }
    }
}



struct LuckyBoxStateView: View, Equatable {
    var text: String
    
    static func == (lhs: LuckyBoxStateView, rhs: LuckyBoxStateView) -> Bool {
        
        return lhs.text == rhs.text
    }
    
    
    var body: some View {
        print("init")
        
        return VStack(alignment: .center) {
            Text("🎊 당첨 🎊")
            
            Spacer()
                .frame(height: 20)
            
            Text(text)
                .font(.system(size: 40, weight: .semibold))
        }
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

- 해당 코드를 실행했을 때의 결과

제대로 적용 된 케이스

보다시피 값이 동일할 경우 init을 수행하지 않음.

 

🚨 여기서 주의해야 하는 점 🚨

이거 진짜 중요함. 경우에 따라서는 EqutableView로 만들어도 작동하지 않는 경우도 존재.

 

- 만약 LuckyBoxStateView에 member가 POD 타입만 존재하는 경우 경우에는 EqutableView임에도 해당 `static func ==`이 호출되지 않고 LuckyBoxStateView의 init이 호출됩니다.


- non-POD가 하나라도 존재하는 경우 해당 부분은 `static func ==`으로 관리되며 @State와 @Biding의 경우에는 내부에서 자체적으로 `static func ==`을 결정하고 있습니다.

 

 

(참고)

https://ios-development.tistory.com/1111

https://velog.io/@doldamul/SwiftUI-Modifier-equatable

https://swiftui-lab.com/equatableview/