# 1. 클래스 정의
import Foundation
import SwiftUI
class Memo: Identifiable, ObservableObject {
let id: UUID
@Published var content: String
let insertDate: Date
init(content: String, insertDate: Date = Date.now) {
self.id = UUID()
self.content = content
self.insertDate = insertDate
}
}
SwiftUI에서 제공하는 Identifiable
프로토콜, ObservableObject
프로토콜을 채택한 클래스를 정의한다. 객체 속성 정의 시 값의 변화에 따라 UI에 실시간 바인딩이 필요한 속성은 @Published
키워드로 선언한다.
# 2. DAO 정의
코어데이터로의 저장 로직을 처리할 DAO 클래스를 정의한다.
class MemoStore: ObservableObject {
@Published var list: [Memo]
init() {
// 더미데이터
self.list = [
Memo(content: "day1"),
Memo(content: "day2", insertDate: Date.now.addingTimeInterval(3600 * -24)),
Memo(content: "day3", insertDate: Date.now.addingTimeInterval(3600 * -48))
]
}
func insert(memo: String) {
list.insert(Memo(content: memo), at: 0)
}
func update(memo: Memo?, content: String) {
guard let memo = memo else { return }
memo.content = content
}
func delete(memo: Memo) {
list.removeAll { $0.id == memo.id }
}
func delete(set: IndexSet) {
for index in set {
list.remove(at: index)
}
}
}
메모 리스트 속성의 경우 List
컴포넌트에 실시간 UI 바인딩을 위해 @Published
로 선언한다.
# 3. 메인화면 정의
struct MainListView: View {
@EnvironmentObject var store: MemoStore
var body: some View {
NavigationView {
List(store.list) { memo in
VStack(alignment: .leading) {
Text(memo.content)
.font(.body)
.lineLimit(1)
Text(memo.insertDate, style: .date)
.font(.caption)
.foregroundStyle(.secondary)
}
}
.navigationTitle("메모")
}
}
}
엔트리포인트가 되는 메인 뷰를 정의한다. @EnvironmentObject
키워드 선언을 통해 하위 뷰에서 접근할 DAO
속성을 추가한다.
@EnvironmentObject
키워드로 선언된 속성은 자식 뷰에서 부모 뷰의 envorionmentObject에 접근할 수 있게 된다.
NavigationView
로 전체 뷰를 래핑하고 뷰 안에 List
를 추가하여 테이블 뷰를 삽입한다. 생성자 파라미터에 바인딩할 속성을 전달하고 내부 컴포넌트에 속성값들을 연결해준다.
navigationTitle
모디파이어를 통해 네비게이션 뷰의 타이틀값을 조정할 수 있다.
# 4. 메인화면 리팩토링
import SwiftUI
struct MainListView: View {
@EnvironmentObject var store: MemoStore
var body: some View {
NavigationView {
List(store.list) { memo in
MemoCell(memo: memo)
}
.navigationTitle("메모")
}
}
}
struct MemoCell: View {
@ObservedObject var memo: Memo
var body: some View {
VStack(alignment: .leading) {
Text(memo.content)
.font(.body)
.lineLimit(1)
Text(memo.insertDate, style: .date)
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
VStack
에서 처리하던 뷰를 별도의 구조체로 분리한다. VStack
클로저 호출 단에서 우클릭하여 Extract Subview
를 선택하면 자동으로 새로운 구조체를 정의해준다.
새로 정의된 셀 구조체에 바인딩할 데이터를 속성으로 추가하는데 @ObservedObject
키워드를 붙여 속성 업데이트가 이루어질 때 자동으로 UI 업데이트를 진행하게 된다.