-
SwiftData 기본 사용법 Model, ModelContext, Query개발/Swift 2025. 5. 8. 09:03
엔티티 정의
@Model final class User: Identifiable { var name: String var email: String @Attribute(.externalStorage) var imageData: Data? init(name: String, email: String, imageData: Data? = nil) { self.name = name self.email = email self.imageData = imageData } }
@Attribute 매크로 써서 외부 저장소에 저장가능
- 좀 느리지만 큰 데이터 다루는 용도
- 일반적인 내부데이터는 SQLite DB 사용하지만 이것은 Document 폴더에 들어간다고 함
- externalStorage 실제 경로 ⇒ 앱의 Documents 디렉토리 내 별도 폴더
- ~/Library/Application Support/[앱 번들 ID].store/external_storage/
- 일반 데이터 (ex> name, email ) ⇒ 앱의 Application Support 디렉토리에 저장됨
- ~/Library/Application Support/[앱번들ID].store/
- externalStorage 실제 경로 ⇒ 앱의 Documents 디렉토리 내 별도 폴더
context
CRUD 작업을 가능하게 하는 객체
@Environment(\\.modelContext) private var context
일반적으로 view에서는 위와 같이 매크로를 통해 쉽게 접근 가능함
근데 초기에 model container 세팅해줘야함
WindowGroup { ContentView() } .modelContainer(for: User.self) // SwiftData의 저장소 시스템을 초기화하고 활성화
데이터 타입이 여러개라면 schema에 넣어주면됨,
그외 버전관리, 옵션 config 세팅도 가능함
private var modelContainer: ModelContainer = { let schema = Schema([User.self]) //여기 다른 타입 추가 let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \\(error)") } }() WindowGroup { MyPageView(store: Store(initialState: .init(), reducer: { AppCoordinator()})) } .modelContainer(modelContainer)
Query
@Query private var users: [User] private var user: User? { users.first }
위와같이 단일 데이터만 필요한 경우에도 배열로 쿼리가가능
- Reducer 에서는 불러올수 없었음, 이외 ViewModel이나 Repository 파일 같이 분리된 파일도 마찬가지임
→ SwiftUI.View 에서만 동작함 , modelContext 도 마찬가지로 View에서만 불러올수 있음
Accessing Environment<ModelContext>'s value outside of being installed on a View. This will always read the default value and will not update.
디자인 패턴 or 아키텍쳐 적용시 ModelContext 를 VM이나 RP에 전달하여 사용할수있겠지만 (or Reducer)
이러 저러한 문제가 발생할수 있으며 Query 매크로 자체를 못쓰기 때문에
Query 매크로 등등 이점을 활용하기가 어려워짐, 따라서 View에서 그냥 접근하는게 낫다고 판단이 됨.
정렬
@Query(sort: \\Keyword.date, order: .reverse) private var keywords: [Keyword]
날짜 오름차순 정렬인경우 위와같이 하면됨
\.date 키패스로 접근하려했지만 Cannot infer key path type from context; consider explicitly specifying a root type 이런 에러가 발생하여 명시적으로 타입을 넣어줌
실시간 데이터
Query로 불러온 데이터가 저장을 통해 새로운 데이터가 들어왔을떄 API나 coredata 같은 경우는 refresh를 통해 새로운 값을 다시 불러와야했다.
@Query private var users: [User]
근데 쿼리를 사용하면 알아서 실시간 데이터를 대응해줌
“ObservableObject와 Publisher를 사용하여, 데이터베이스의 변경을 감지하면 뷰를 다시 그리도록 트리거합니다” 라고 함
데이터 수정
@Query private var users: [User] private var user: User? { users.first } @Environment(\\.modelContext) private var context
위와같이 간편하게 context 가져옴
func editUser() { user?.name = "james" try? context.save() }
user.name 을 변경하면 메모리 차원에서 변경이 적용됨, (ex>페이지 떠있을떄)
context.save() 하는 순간 영구 저장소에 저장 (커밋) 됨
아주 쉽고 간편하게 변경이 됨
파라미터를 던져주는것도 아닌데 어떻게 변경된것을 알고 저장할까?
class ModelContext { // 내부적으로 이런 식의 변경사항 추적 private var changedObjects: Set<PersistentModel> private var insertedObjects: Set<PersistentModel> private var deletedObjects: Set<PersistentModel> } // 사용 예시 @Environment(\\.modelContext) private var context func example() { let user = User(name: "Kim") context.insert(user) // insertedObjects에 추가 user.name = "Park" // changedObjects에 추가 context.delete(user) // deletedObjects에 추가 try? context.save() // 모든 변경사항 한번에 저장 }
이러게 변경 사항들을 보관하고있다가 일괄 저장 한다 함
'개발 > Swift' 카테고리의 다른 글
TextEditor 자동 스크롤 오류, cursor 위치 따라가기 (0) 2025.04.18 긴 text 내용이 minHeight에 잘리는 문제 (onAppear, task) (0) 2025.04.18 Observable → Async / Await 으로 변환하여 API 처리하기 (AsyncThrowingStream, withCheckedThrowingContinuation) (0) 2025.04.16 SwiftUI TextEditor에 키보드 toolbar가 노출되지 않는 오류 (0) 2025.04.16 [Swift] 애니메이션 정지 재생 CGAffineTransform (0) 2025.04.11