iOS

[SwiftUI] Managing Model Data

빨간체리반지 2023. 12. 11. 15:54

@State

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct State<Value> : DynamicProperty {
    public init(wrappedValue value: Value)
    public init(initialValue value: Value)
    
    public var wrappedValue: Value { get nonmutating set }
    public var projectedValue: Binding<Value> { get }
}

var 로 선언되어야함. private 으로 사용하는것 권고. (single source of truth)

SwiftUI 는 State 로 선언된 모든 프로퍼티의 스토리지를 관리.

State 변수값이 변하면 view 의 appearance 를 무효화하고, body 를 다시 계산한다.

State 로 선언된 instance 는 value 자체가 아님. 값에 접근시 value 프로퍼티 사용해야 함.

특정 view 에 속하고, 외부에서 절때 접근하면 안되는 간단한 프로퍼티 선언시에 적합.

 

아래로 선언해도 filteredLandmarks 변화 detect 해 view 뿌림.

    @State private var showFavoritesOnly = true

    var filteredLandmarks: [Landmark] {

        landmarks.filter { landmark in

            (!showFavoritesOnly || landmark.isFavorite)

        }

    }

 

@State 프로퍼티는 항상 private 으로 선언하고, 가장 상위뷰에서 @State 를 관리할 것.

(외에서 initialized 되는것을 방지하기 위함)

 

자식 뷰에 데이터를 넘길 땐,

read-only 로 바로 전달하거나 read-write 을 위해선 binding 형태로 전달해야한다.

read-write 이 가능한 binding 형태에서만 부모뷰의 뷰를 업데이트할 수 있다.

 

@Binding

read & write

State 변수를 다른 뷰에 전달할 때 사용. $ 를 사용해 Binding 을 얻을 수 있음.

State 변수가 변경되면 @Binding 변수에서 인지하고 바로 뷰에 반영.

외부에서 접근해야하기 때문에 private 사용하지 않음.

 

You use the $ prefix to access a binding to a state variable, or one of its properties.

$ prefix 는 binding(projectedValue) 접근시 사용.

var projectedValue: Binding<Value>

wrappedValue, projectedValue

 

protocol ObservableObject: AnyObject

실제로 값이 변경되기 전에 신호를 주는 objectWillChange Publisher 를 제공한다.

해당 프로토콜을 채택한 class 의 instance 는 값이 변경되면 뷰가 업데이트 된다.

// 방법 1)
final class CounterViewModel: ObservableObject {
    @Published var count = 0

    func incrementCounter() {
        count += 1
    }
}

// 방법 2)
// 여러값을 변경 한 후, 수동으로 신호를 쏘고 싶을 때 유용.
final class CounterViewModel: ObservableObject {
    private(set) var count = 0

    func incrementCounter() {
        count += 1
        // ObservableObject 가 제공하는 objectWillChange 로 직접 신호를 보낼 수 도 있다.
        objectWillChange.send()
    }
}

 

@Published

ObservableObject 내에서 property 를 선언할 때 사용.

해당 속성이 업데이트 될 때 뷰를 업데이트 시킴.

 

@ObservedObject

ObservableObject 를 구독하고 값이 업데이트 될 때 뷰를 갱신.

여러 프로퍼티 및 메소드가 있거나 여러 view 에서 공유할 수 있는 커스텀 타입이 있는 경우 State 대신 사용.

아래처럼 ViewModel 클래스 인스턴스를 @ObservedObject 로 선언해 ViewModel 내의 @Published 프로퍼티를 감지할 수 있다.

final class CounterViewModel: ObservableObject {
    @Published var count = 0

    func incrementCounter() {
        count += 1
    }
}

struct CounterView: View {
    @ObservedObject var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            Text("Count is: \(viewModel.count)")
            Button("Increment Counter") {
                viewModel.incrementCounter()
            }
        }
    }
}

 

@StateObject

ObservableObject 를 구독하고 값이 업데이트 될 때 뷰를 갱신.

사용시 @State 처럼 private 으로 사용하면 됨.

StateObject 와 달리 STate 는 항상 선언시 default value 로 initialized 되어있어야 한다.

SwiftUI 는 해당 모델의 instance 를 단 한번만 생성한다.

(= 뷰가 갱신되어도 instance 를 새로 생성하지 않음. 단, view 자체가 새로 생성되는 케이스는 제외)

 

@ObservedObject vs @StateObject

@StateObject 는 @ObservedObject 와 달리 view 의 body 가 재구성되어도 파괴되지 않음.

SwiftUI view 가 재구성될 가능성이 있는 경우 @ObservedObject 를 쓰는것은 안전하지 않음.

단, @StateObject 를 다량 사용시 여러군데에서 객체의 라이프사이클을 관리하게 됨.

(Apple 권고사항)

최초 초기화 시에는 StateObject 를 사용하고, 이미 객체화된 것을 넘겨 받을 땐 ObservedObject 를 사용.

 

@EnvironmentObject

ObservableObject 를 구독하고 값이 업데이트 될 때 뷰를 갱신.

reference 타입.

모든 view 에서 읽을 수 있는 shared 데이터.

프로퍼티가 변경되면 EvironmentObject 가 바인딩되고 있는 모든 view 가 자동 업데이트 됨.

단하나의 인스턴스만 사용될 수 있다.

.environmentObject(:_) 로 적용 가능.

 

@Environment

value 타입. (String, Int, Struct...)

key-value.

기본 제공되는 Environment 프로퍼티가 있다.

@Environment(\.locale) var locale: Locale

 

.environment() 로 적용 가능.

 

struct 로 EnvironmentKey 를 선언하면 custom 한 Environment 선언도 가능하다.

struct SunlightKey: EnvironmentKey {
    static var defaultValue: Double = 1.09
}

extension EnvironmentValues {
    var sunlight: Double {
        get { self[SunlightKey.self] }
        set { self[SunlightKey.self] = newValue }
    }
}

@Environment(\.sunlight) var sunlight // 이렇게 사용 가능.

 

 

@Observable (iOS 17.0+)

Observable() 매크로

SwiftUI 는 observable 프로퍼티가 변경되거나 body 에서 property 를 직접 읽을때에만 view 를 갱신시킨다.

 

@Bindable (iOS 17.0+)

body 프로퍼티 내에서 Bindable 래퍼로 감싸 사용

 


 

 

https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

'iOS' 카테고리의 다른 글

[iOS] Git 명령어  (0) 2023.11.28
[iOS] 공부 링크 (ing...)  (0) 2022.02.07
[iOS] Rendering  (0) 2020.12.31
[iOS] Swift 기본 문법 (YouTube - yagom)  (0) 2020.10.26
[iOS] Nine-Patch, 둥근모서리 이미지 소스 사용하기  (0) 2020.10.20