반응형
이번 프로젝트는 가계부 프로젝트!
이름은 iExpense 애플 스러운 이름이다.
소스를 보자. 2개의 파일이 추가되었다.
//
// ContentView.swift
// iExpense
//
// Created by HanTJ on 12/1/24.
//
import SwiftUI
struct ExpenseItem: Identifiable, Codable { //저장을 위한 Codable
var id = UUID() //UUID자동생성!
let name: String
let type: String
let amount: Double
}
@Observable
class Expenses {
var items = [ExpenseItem]() {
didSet {
// persistent 저장!
if let encoded = try? JSONEncoder().encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
//처음 클래스 생성시 persistent 를 복원
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from:savedItems) { //self는 꼭 써야하는건가..
items = decodedItems
return
}
}
items = [] //에러방지! 오류나면 빈값으로 초기화
}
}
struct ContentView: View {
@State private var expenses = Expenses()
@State private var showingAddExpense = false
var body: some View {
NavigationStack {
List {
ForEach(expenses.items) { item in //id항목 않써줘도 된다 why? Identifiable 프로토콜을 구현했기 때문에!
HStack {
VStack {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text(item.amount, format: .currency(code: "USD"))
}
}
.onDelete(perform: removeItems)
}
.navigationTitle("iExpense")
.toolbar {
Button("Add Expense", systemImage: "plus") {
showingAddExpense = true
}
}
.sheet(isPresented: $showingAddExpense) {
AddView(expenses: expenses)
}
}
}
//delete item의 공식같이 익혀두자.
func removeItems(at offsets: IndexSet) {
expenses.items.remove(atOffsets: offsets)
}
}
#Preview {
ContentView()
}
클래스와 구조체를 이용해서 기본 저장 구조를 만들었다.
클래스에 Observable 을 쓰고 , 클래스 초기화와 내부 변수의 할당후 로직으로 저장을 만들었다. 위치는 UserDefaults 영역
그리고 드뎌! 새로운 뷰를 호출하는 방법을 했다. sheet modifier 를 이용했고, showingAddExpense 라는 바인드 변수로 보이고 안보이고를 선언했다. 그리고 뷰를 생성하면 ㅇㅋ
추가된 화면인 AddView 이다.
//
// AddView.swift
// iExpense
//
// Created by HanTJ on 12/4/24.
//
import SwiftUI
struct AddView: View {
@State private var name = ""
@State private var type = "Personal"
@State private var amount = 0.0
@Environment(\.dismiss) var dismiss
let types = ["Business", "Personal"]
var expenses: Expenses
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $name)
Picker("Type", selection: $type) {
ForEach(types, id: \.self) {
Text($0)
}
}
TextField("Amount", value: $amount, format: .currency(code: "USD"))
.keyboardType(.decimalPad)
}
.navigationTitle("Add new expense") //. 빼먹었떠니 컴파일 오류는 아닌데 실행이 안되더라.. 오호..
.toolbar {
Button("Save") {
let item = ExpenseItem(name: name, type: type, amount: amount)
expenses.items.append(item) //신규로 만들걸 저장한다. 메인 ContnetView에 있는 expenses에 하는것!
dismiss()
}
}
}
}
}
#Preview {
AddView(expenses: Expenses())
}
그동안 공부했던거 + 추가 뷰를 닫기 위한 dismiss 사용법이다. 데이터를 공유하기 위해 생성시 expenses 클래스를 참조변수로 받았다.
그리고 Preview도 생성 인수가 필요하니, 빈값으로 Expenses 클래스 생성을 추가 했다!
반복하니 먼가 슬슬 익숙하다!
반응형