iOS/SwiftUI

[SwiftUI] @State, @Binding, @AppStorage, @SceneStroage, @FetchRequest

_Rey 2022. 4. 18. 23:45

SwiftUI 에서 사용되는 Property Wrappers 를 정리해보자.

 

@State

SwiftUI 관련해서 강좌나 예제 코드를 보면 거의 처음 마주하는 키워드다.

https://developer.apple.com/documentation/swiftui/state

변수 값이 바뀌면 변수와 연결된 View를 업데이트 한다.

struct PlayButton: View {
    @State private var isPlaying: Bool = false

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

RxSwift 의 Observe 기능을 한줄로 줄여버렸다.

 

@Binding

@State 변수를 자식 뷰로 전달해 값을 수정할 때 사용한다. 부모 뷰는 State를, 자식 뷰는 Binding으로 선언한다.

자식 뷰에서 Binding 값이 변경되면, 연결된 부모 뷰의 State 값도 변경되며, View가 업데이트된다.

struct PlayButton: View {
    @Binding var isPlaying: Bool

    var body: some View {
        Button(isPlaying ? "Pause" : "Play") {
            isPlaying.toggle()
        }
    }
}

struct PlayerView: View {
    var episode: Episode
    @State private var isPlaying: Bool = false

    var body: some View {
        VStack {
            Text(episode.title)
                .foregroundStyle(isPlaying ? .primary : .secondary)
                // Pass a binding.
                // $ 기호를 붙여 Binding 변수로 전달한다.
            PlayButton(isPlaying: $isPlaying) 
        }
    }
}

주로 부모-자식처럼 여러 뷰에서 공유되는 데이터보다는 2~3개의 뷰에서 공유되는 데이터에 많이 사용하는듯 하다.

 

@AppStorage

UserDefaults를 사용해봤다면 이해하기 쉽다.

// UserDefaults에 값 저장
UserDefaults.standard.set({Value},forKey: "USER_DEFAULTS_KEY")

// UserDefaults에 저장된 값 호출
UserDefaults.standard.value(forKey: "USER_DEFAULTS_KEY")

해당 UserDefaults 기능에 @State를 합친 기능을 한다.

// 해당 키로 저장된 값을 불러와 AppStorageValue 저장 만약 값이 nil일 경우 0으로 초기화 및 저장
@AppStorage("APP_STORAGE_KEY") private var AppStorageValue = 0
...
// 변수에 새롭게 저장된 값을 키로 UserDefaults에 동기화 및 연결된 View 업데이트
AppStorageValue = 10

UserDefaults에 저장할 뿐 아니라 해당 값에 의존된 View까지 업데이트해준다.

 

@SceneStorage

iOS 14 부터 사용이 가능하다. Scene의 개념이 필요하기에 사실상 iPadOS에서 주로 사용된다.

iPadOS에서 Multiple windows 옵션이 true일 경우, 여러 Window를 띄울 수 있게 되는데 해당 Window를 하나의 Scene이라고 한다.

// Scene(Window)이 여러개 일때, 모든 윈도우 프로세스에서 동기화
// ex) Scene1에서 해당 값을 변화할때, Scene2에서도 값 변화 및 의존되는 View 업데이트
@AppStorage("APP_STORAGE_KEY") private var AppStorageValue = 0

// Key가 같더라도 각 Scene(Window)마다, 다른 상태 값을 저장
// Scene(Window)마다 값의 영향을 주지 않음
@SceneStorage("SCENE_STORAGE_KEY") private var SceneStorageValue = 0

iOS에서는 Multiple windows 옵션이 없기 때문에 사실상 @AppStorage와 @SceneStroage가 큰 차이가 없다.

따라서 iPadOS 중에서도 Multiple windows 옵션이 존재하는 앱을 개발한다면 신경써야할 문제이다.

 

@FetchRequest

@AppStorage와 @SceneStroage가 UserDefaults와 관련된 키워드 였다면, @FetchRequest는 CoreData와 관련이 있다.

CoreData에서 Entity를 사용하기 전에 부모 뷰로부터 환경변수를 설정해줘야한다.

class DataController: ObservableObject {
    var container: NSPersistentContainer
    
    init(containerName: String) {
        self.container = NSPersistentContainer(name: containerName)
        self.container.loadPersistentStores { description, error in
            if let error = error {
                print("Core Data Failed to load: \(error.localizedDescription)")
                return
            }
            self.container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
        }
    }
}

...

@StateObject private var dataController = DataController(containerName: "{SC_DATA_MODELD}")

...

ContentView()
	.environment(\.managedObjectContext,
   		self.dataController.container.viewContext)
// 부모 View로부터 받은 환경 변수
@Environment(\.managedObjectContext) var moc

@FetchRequest(sortDescriptors: []) var fetchedResults: FetchedResults<{COREDATA_ENTITY}>
// 또는
// predicate: 필터 또는 정렬 적용
@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "{PREDICATE}")) var fetchedResults: FetchedResults<{COREDATA_ENTITY}>

@AppStorage, @SceneStroage 처럼 CoreData를 동기화 할 수 있다.

@StateObject, @Environment처럼 아직 모르는 개념이 있지만 계속해서 알아보자.