[iOS] SwiftUI의 다양한 Property Wrappers

z-wook·2024년 1월 7일
post-thumbnail

@State

  1. SwiftUI에서 상태를 처리하는 방법
  2. 뷰의 상태를 저장하는 프로퍼티로 상태 관리 주체는 해당 뷰
  3. 기본적으로 Private 선언이기에 다른 뷰와 값을 소통하려면 Binding을 이용
  4. 값이 변경될 때마다 UI 업데이트
  • 예시
import SwiftUI

struct ContentView: View {
    @State private var isPlaying: Bool = true
    
    var body: some View {
        Button(isPlaying ? "Play" : "Pause") {
            isPlaying.toggle()
        }
    }
}

@Binding

  1. 뷰와 상태를 바인딩 하는 방법
  2. 상위 @State 변수를 전달 받아 하위 뷰에서 캐치에 변화 감지 및 연결
  3. Binding은 다른 뷰가 소유한 속성을 연결하기에 소유권 및 저장 공간이 없음
struct PlayerView: View {
    var movie: Movie
    @State private var isPlaying: Bool = false
    
    var body: some View {
        VStack {
            Text(movie.title)
                .foregroundStyle(isPlaying ? .primary : .secondary)
            PlayButton(isPlaying: $isPlaying)
        }
    }
}
struct PlayButton: View {
    @Binding var isPlaying: Bool
    
    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

ObservableObject

  1. 클래스 프로토콜로 관찰하는 어떠한 값이 변경되면 변경사항을 알려줌
  2. 뷰에서 인스턴스 변화를 감시하기 위해 뷰 모델 객체로 생성할 때 사용할 수 있음
  • 예시
class Contact: ObservedObject {
    @Published var name: String
    @Published var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func haveBirthday() -> Int {
        age += 1
        return age
    }
}
let han = Contact(name: "Han", age: 20)
cancellable = han.objectWillChange
	.sink { _ in
		print(han.age)
	}
print(han.haveBirthday())

@Published

  1. ObservableObject를 구현한 클래스 내에서 프로퍼티 선언 시 사용
  2. @Published로 선언된 프로퍼티를 뷰에서 관찰할 수 있음
  3. ObservableObject의 objectWillChange.send() 기능을 @Published 프로퍼티가 변경되면 자동으로 호출

@ObservedObject

  1. 뷰에서 ObservableObject 타입의 인스턴스 선언 시 사용
  2. ObservableObject의 값이 업데이트되면 뷰를 업데이트
  3. 부모 View의 변경이 일어나면 초기화된다.
  • 예시
class User: ObservableObject {
    @Published var age = 20
}
struct ContentView: View {
    @ObservedObject var user: User
    
    var body: some View {
        Button("Get Age") {
            user.age += 1
        }
    }
}

@StateObject

  1. 뷰에서 ObservableObject 타입의 인스턴스 선언 시 사용
  2. 뷰마다 하나의 인스턴스를 생성하며, 뷰가 사라지기 전까지 같은 인스턴스 유지
  3. @ObservedObject의 뷰 렌더링 시 인스턴스 초기화 이슈 해결을 위한 방법
  4. 매번 인스턴스가 새롭게 생성되는 것처럼 외부에서 주입을 받는 경우가 아닌 최초 생성 선언 시에 @StateObject를 사용하는 것이 적절한 방법
  5. 부모 View의 변경에도 초기화되지 않는다.

@ObjervedObject와 @StateObject 차이점

@ObjervedObject는 상태 변경이 있을 때 뷰를 다시 생성해서 그리지만, @StateObject는 뷰를 다시 생성하지 않고 항상 동일한 뷰를 사용(효율)

※ Tip
기본적으로 @StateObject를 사용하되, 해당 프로퍼티를 subview에게도 주입시켜야 한다면, @ObservedObject를 사용하는 것이 좋다.

  • subview에 @StateObject 프로퍼티를 주입하면, 해당 @StateObject의 수명 주기가 두 곳에서 관리가 되므로 의존성을 줄이기 위해 @ObservedObejct를 사용하는 것이 좋다.

@Environment

  1. 미리 정의되어 있는 시스템 공유 데이터
  2. 사용하려는 공유 데이터의 이름을 keyPath로 전달하여 사용
  3. 시스템 공유 데이터는 가변 하기에 var로 선언 필요
  4. 뷰가 생성되는 시점에 값이 자동으로 초기화됨
  • 예시
struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        Text("Hello, World!!!")
            .foregroundStyle(colorScheme == .dark ? .white : .black)
    }
}

@EnvironmentObject

  1. ObservableObject를 통해 구현된 타입의 인스턴스를 전역적으로 공유하여 사용
  2. 앱 전역에서 공통으로 사용할 데이터를 주입 및 사용

남발하면 데이터 의존성에 문제가 발생(코드 유지 보수 어렵고 버그 유발)
데이터의 일관성을 유지하기 힘들다.(데이터 관리의 복잡도 증가)
@EnvironmentObject에 의존하면 단위 테스트, UITest 작성하기 어려워진다.

  • 예시
class Info: ObservableObject {
    @Published var num = 20
}
@main
struct SwiftUI_TestApp: App {
    var body: some Scene {
        WindowGroup {
            MainView()
            .environmentObject(Info())
        }
    }
}
struct MainView: View {
    @EnvironmentObject var info: Info
    
    var body: some View {
        Button(action: {
            self.info.num += 1
        }, label: {
            Text("Plus Number")
        })
        SubView()
    }
}
struct SubView: View {
    @EnvironmentObject var info: Info
    
    var body: some View {
        Button(action: {
            self.info.num -= 1
        }, label: {
            Text("Minus Number")
        })
    }
}

참고 자료

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

profile
🍎 iOS Developer

0개의 댓글