// 데이터 모델 부분
struct FruitModel: Identifiable {
let id: String = UUID().uuidString
let name: String
let count: Int
}
struct ViewModelStudy: View {
@State var fruitArray: [FruitModel] = [
FruitModel(name: "Apple", count: 5)
]
// 인터페이스 뷰 부분
var body: some View {
NavigationStack {
List {
ForEach(fruitArray) { fruit in
HStack {
Text("\(fruit.count)")
.foregroundColor(.red)
Text(fruit.name)
}
}
.onDelete { indexSet in
deleteFruit(index: indexSet)
}
}
.onAppear {
getFruits()
}
}
}
// 로직 및 기능 부분
func getFruits() {
let fruit1 = FruitModel(name: "Banana", count: 7)
let fruit2 = FruitModel(name: "Carrot", count: 3)
fruitArray.append(fruit1)
fruitArray.append(fruit2)
}
func deleteFruit(index: IndexSet) {
fruitArray.remove(atOffsets: index)
}
}
MVVM Architecture
SwiftUI에서는 보통 MVVM 디자인 패턴에 따라 코드를 분리하여 관리한다.
Array
, Struct
등으로 분리하여 관리한다.struct FruitModel: Identifiable {
let id: String = UUID().uuidString
let name: String
let count: Int
}
Class
를 통해 분리하여 관리한다.ObservableObject
프로토콜을 채택(Class에서만 가능)하여 데이터 모델을 관찰하고 변경사항을 뷰에 알리는 역할을 한다.@Published
를 통해 선언한다.@Published
해당 객체에 변경사항이 있을 때 자동으로 뷰에 알리고, 뷰를 업데이트하는 역할을 한다.
class FruitViewModel: ObservableObject {
// 데이터 모델 관찰
@Published var fruitArray: [FruitModel] = [
FruitModel(name: "Apple", count: 5)
]
init() {
getFruits()
}
func getFruits() {
let fruit1 = FruitModel(name: "Banana", count: 7)
let fruit2 = FruitModel(name: "Carrot", count: 3)
fruitArray.append(fruit1)
fruitArray.append(fruit2)
}
func deleteFruit(index: IndexSet) {
fruitArray.remove(atOffsets: index)
}
}
View
를 통해 분리하여 관리한다.@ObservedObjcet
를 통해 관찰 가능한 인스턴스로 생성한다.struct ViewModelStudy: View {
// 뷰모델이 관찰 가능한 인스턴스
@ObservedObject var fruitViewModel: FruitViewModel = FruitViewModel()
var body: some View {
NavigationStack {
List {
ForEach(fruitViewModel.fruitArray) { fruit in
HStack {
Text("\(fruit.count)")
.foregroundColor(.red)
Text(fruit.name)
}
}
.onDelete(perform: fruitViewModel.deleteFruit)
}
}
}
}
@ObservedObject
와 @StateObject
모두 기본적으로 뷰 내부에서 관찰 가능한 객체의 인스턴스를 저장하기 위해 사용된다.차이 | @ObservedObject | @StateObject |
---|---|---|
데이터 변화 | 데이터 변화가 있을 때 뷰를 처음부터 다시 그린다. | 데이터 변화가 있을 때 변화가 있는 부분의 뷰만 다시 그린다. |
생명주기 | 뷰가 사라지고 다시 그려짐에 따라, 인스턴스도 사라지고 다시 생성된다. | 초기에 인스턴스가 생성되고 뷰와 상관없이 유지된다. |
사용 | 보통 다루는 데이터가 달라지는 하위 뷰에서 사용한다. | 보통 데이터를 유지해야하고 초기에 생성되는 상위 뷰에서 사용된다. |
struct ViewModelStudy: View {
// 초기에 생성되는 상위 뷰이므로 @StateObject 인스턴스
@StateObject var fruitViewModel: FruitViewModel = FruitViewModel()
var body: some View {
NavigationStack {
List {
ForEach(fruitViewModel.fruitArray) { fruit in
HStack {
Text("\(fruit.count)")
.foregroundColor(.red)
Text(fruit.name)
}
}
.onDelete(perform: fruitViewModel.deleteFruit)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink {
// 하위 뷰 이동 및 동일한 뷰모델(데이터) 전달
RandomScreen(fruitViewModel: fruitViewModel)
} label: {
Image(systemName: "arrow.right")
}
}
}
}
}
}
struct RandomScreen: View {
// 하위 뷰이므로 @ObservedObject 인스턴스
@ObservedObject var fruitViewModel: FruitViewModel
@Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
ForEach(fruitViewModel.fruitArray) { fruit in
Text(fruit.name)
}
}
}
}