앱을 껐다 켜도 데이터가 남아야 한다
// Codable = Encodable + Decodable
struct User: Codable {
let id: Int
let name: String
let email: String
}
// Swift → JSON (Encoding)
let user = User(id: 1, name: "이현호", email: "test@test.com")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let data = try? encoder.encode(user),
let json = String(data: data, encoding: .utf8) {
print(json)
// { "id": 1, "name": "이현호", "email": "test@test.com" }
}
// JSON → Swift (Decoding)
let jsonString = """
{ "id": 1, "name": "이현호", "email": "test@test.com" }
"""
let decoder = JSONDecoder()
if let data = jsonString.data(using: .utf8),
let decoded = try? decoder.decode(User.self, from: data) {
print(decoded.name) // 이현호
}
user_name인데 Swift 속성을 userName으로 쓰려면 어떻게 하는가? (CodingKeys)try?와 try!의 차이는 무엇인가? 실제 앱에서 어느 쪽이 더 안전한가?Encodable + Decodable의 타입 별칭. struct의 모든 프로퍼티가 Codable이면 자동으로 구현이 생성된다keyDecodingStrategy로 snake_case ↔ camelCase 자동 변환 가능case userName = "user_name"// 기본 타입 저장/불러오기
UserDefaults.standard.set(true, forKey: "isDarkMode")
UserDefaults.standard.set(42, forKey: "launchCount")
UserDefaults.standard.set("한국어", forKey: "language")
let isDark = UserDefaults.standard.bool(forKey: "isDarkMode")
let count = UserDefaults.standard.integer(forKey: "launchCount")
// Codable 구조체 저장 (JSON으로 변환 후 Data로 저장)
struct AppSettings: Codable {
var theme: String
var fontSize: Int
}
let settings = AppSettings(theme: "dark", fontSize: 16)
if let data = try? JSONEncoder().encode(settings) {
UserDefaults.standard.set(data, forKey: "appSettings")
}
// 불러오기
if let data = UserDefaults.standard.data(forKey: "appSettings"),
let saved = try? JSONDecoder().decode(
AppSettings.self, from: data) {
print(saved.theme) // dark
}
// @AppStorage — SwiftUI 전용 UserDefaults
struct ContentView: View {
@AppStorage("isDarkMode") var isDarkMode = false
var body: some View {
Toggle("다크 모드", isOn: $isDarkMode)
}
}
@AppStorage와 @State의 가장 큰 차이는 무엇인가?@State처럼 바인딩으로 사용. 값이 바뀌면 자동으로 View가 다시 그려진다import SwiftData
// @Model 매크로로 영구 저장 모델 선언
@Model
class TodoItem {
var title: String
var isCompleted: Bool
var createdAt: Date
init(title: String) {
self.title = title
self.isCompleted = false
self.createdAt = Date()
}
}
// 앱 진입점에서 ModelContainer 연결
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: TodoItem.self)
}
}
@Model은 왜 struct가 아닌 class여야 하는가?.modelContainer(for:)를 최상위 App에 달면 하위 모든 View에서 사용할 수 있는 이유는 무엇인가?.modelContainer를 어떻게 설정하는가?.modelContainer(for: [A.self, B.self])로 여러 모델 등록 가능@Environment(\.modelContext)로 View에서 접근. insert / delete / savestruct TodoListView: View {
// @Query: DB에서 자동으로 데이터를 가져오고
// 변경 시 View를 자동으로 업데이트
@Query(sort: \TodoItem.createdAt, order: .reverse)
var todos: [TodoItem]
// 필터 조건 추가
@Query(filter: #Predicate { !$0.isCompleted },
sort: \TodoItem.createdAt)
var activeTodos: [TodoItem]
var body: some View {
List(todos) { todo in
Text(todo.title)
}
}
}
// ModelContext로 CRUD
struct AddTodoView: View {
@Environment(\.modelContext) private var context
@State private var title = ""
var body: some View {
HStack {
TextField("할 일", text: $title)
Button("추가") {
let item = TodoItem(title: title)
context.insert(item) // Create
title = ""
}
}
}
}
// Delete
func delete(_ item: TodoItem) {
context.delete(item)
}
@Query로 가져온 배열은 @State와 어떻게 다른가? 직접 수정할 수 있는가?context.insert() 후 try context.save()를 해야 하는가, 안 해도 되는가?#Predicate에서 여러 조건을 AND / OR 로 결합하려면?#Predicate<TodoItem> { $0.isCompleted == false }@Model
class Category {
var name: String
// 일대다: 하나의 카테고리에 여러 Todo
@Relationship(deleteRule: .cascade)
var todos: [TodoItem] = []
init(name: String) { self.name = name }
}
@Model
class TodoItem {
var title: String
var isCompleted: Bool
// 역방향 관계
var category: Category?
init(title: String) {
self.title = title
self.isCompleted = false
}
}
// 관계 설정
let category = Category(name: "업무")
let todo = TodoItem(title: "회의 준비")
todo.category = category
context.insert(category)
// deleteRule: .cascade → category 삭제 시 하위 todos도 삭제
deleteRule: .cascade와 .nullify의 차이는? 어느 상황에서 각각 적합한가?// Preview용 인메모리 컨테이너
#Preview {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
let container = try! ModelContainer(
for: TodoItem.self,
configurations: config
)
// 샘플 데이터 삽입
let context = container.mainContext
context.insert(TodoItem(title: "SwiftData 공부"))
context.insert(TodoItem(title: "앱 배포하기"))
return TodoListView()
.modelContainer(container)
}
// 실제 앱 — 영구 저장
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: TodoItem.self)
}
}
isStoredInMemoryOnly: true로 만든 컨테이너는 앱을 재실행하면 데이터가 사라진다. 어떤 상황에서 유용한가?isStoredInMemoryOnly: true