SwiftData

봄이아빠·2024년 9월 14일
0

SwiftUI

목록 보기
1/1
post-thumbnail
post-custom-banner

Apple 공식문서

Swift Data 소개

애플의 설명에 따르면 Swift Data는 @Model을 일반 Swift Type에 사용하여 외부에서 관리할 추가 파일이나 도구 없이 Data를 모델링할 수 있다.
많은 관계를 자동으로 추론 가능하며 @Attribute(.unique)와 같은 명확한 선언을 통해 제약할 수 있다.
Swift Data는 model을 통해 커스텀 스키마를 구축하고 필드를 기본 저장소에 효율적으로 매핑하며, 관리하는 객체는 필요할 때 데이터 베이스에서 가져오고 추가 작업 없이 적절한 때에 자동으로 저장된다. ModelContext를 통해 원하는 대로 제어할 수도 있다.
@Query를 통해 SwiftUI에서 데이터를 fetch할 수 잇으며 데이터의 변경에 대한 실시간 업데이트를 지원하여 수동으로 새로고침을 할 필요가 없다. CloudKit을 사용하여 장치 간 동기화를 할 수 있으며 기존에 사용하던 Core Data와 호환되어 하나의 어플에서 이 둘을 동시에 사용할 수 있다.


FramiOS

//FramiOS님의 블로그 설명이 큰 도움이 됐다.

SwiftData는 Schema와 Container, Context로 이루어져있다.
Schema는 DataBase에 저장 될 데이터의 구조를 정의한다. Schema는 @Model을 class 앞에 붙이는 것으로 만들 수 있다. @Model은 PersistanceModel 프로토콜을 준수하며 PersistanceModel은 Observable을 포함해 AnyObject, Hashable, Identifiabled을 따른다. 여기서 Observable은 내부의 프로퍼티에 @Published를 붙인 것과 같은 동작을 한다. 즉 위의 애플 소개에 나온 것처럼 @Model을 붙임으로서 해당 클래스 객체의 데이터 변화는 View를 자동으로 새로고침하고 변화를 추적할 수 있게 해준다.


Container는 DataBase를 구축하고 접근할 수 있는 역할을 맡는다. Schema에 대한 정보를 .modelConteainer의 파라미터에 넣어 전달한다. 여러 Schema를 배열 형태로 전달할 수 있으며 Container를 직접 정의하고 전달할 수도 있다. 이를 통해 프리뷰에 대한 임시 데이터를 따로 작성하고 프리뷰에서만 작동하는 DataBase를 생성할 수 있다.

struct exampleMenu {
    static let exampleMenus: [Menu] = [
        Menu(menu: "엽떡")
//        Menu(menu: "엽떡 마라"),
//        Menu(menu: "엽떡 로제 마라"),
//        Menu(menu: "뿌링클 + 엽떡")
    ]
}

@MainActor
let sampleContainer: ModelContainer = {
//    let schema = Schema([Menu.self])
    do {
//        let container = try! ModelContainer(for: schema, configurations: [])
        let container = try ModelContainer(for: Menu.self)

        for i in exampleMenu.exampleMenus{
            container.mainContext.insert(i)
        }
        
        return container
    } catch {
        
        fatalError("is Error")
    }
}()

Shcema도 정의하고 container의 파라미터에 전달할 수 있다.
Container는 어플의 모든 부분에서 DataBase를 사용할 수 있도록 주로 WindowGroup 뒤에 .modelContainer(for:)의 형태로 작성한다. 기본 코드로 작성되어있던 코드에도 해당 부분이 작성되어 있었다.

@main
struct CalculatorApp: App {
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Menu.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(sharedModelContainer)
    }
}

sharedModelContainer를 정의하고 정의한 Container를 ContenteView가 포함 된 WindowGroup으로 전달했다.


Context는 DataBase에 대한 CRUD를 지원하는 부분이다.

@Environment(\.modelContext) private var context

위와 같이 context를 선언하고 context.insert(), context.deldet([index]) 등에 모델 인스턴스를 넘겨주는 형태로 DateBase에 데이터를 CRUD 할 수 있다.
DataBase에서 데이터를 가져와 사용하려고 한다면 @Query를 이용하면 되며 이때 filter나 sort에 대한 기능을 사용할 수 있다.
sort 시 sortDiscriptor를 사용할 수 있고 오름차순 또는 내림차순을 정할 수 있다. filter를 사용하는 경우엔 Predicate를 사용할 수 있으며 타입을 제네릭으로 조건식을 클로저로 전달하는 방식으로 사용할 수 있다. 사실 sort와 filter 부분은 아직 공부하지 않은 내용이 많아 확실한 사용법을 이해하진 못했다.
@Query를 사용해 데이터를 받아온 부분을 하위 View에서 사용하고 싶은 경우 기존의 @State와 @Binding의 관계처럼 @Bidable을 사용하면 된다. Bind와 Bindable의 차이는 값 타입과 클래스 타입의 차이라고 한다.


추가적으로 @Relationship은 앞선 애플의 소개에서 나온 많은 관계의 추론의 내용이다. Schema를 구성하면서 2가지 이상의 Model을 일대다 또는 다대다의 형태로 관계를 구축할 수 있다.

@Model
final class Bean {
    var name: String
    var country: Country?
    var roasting: Roasting
    
    init(name: String, roasting: Roasting) {
        self.name = name
        self.roasting = roasting
    }
    
}

extension Bean {
    enum Roasting: String, CaseIterable, Codable {
        case light = "Light Roasting"
        case medium = "Medium Roasting"
        case dark = "Dark Roasting"
    }
}

@Model
final class Country {
    @Attribute(.unique) var country: String
    @Relationship(inverse: \Bean.country)
    var beans = [Bean]()
    
    init(country: String) {
        self.country = country
    }
}

@Relationship 매크로를 사용하여 관계를 표시해줄 수 있다. 관계는 모델 사이에서 일대다 또는 다대다로 구성될 수 있다.
예를 들어 위 코드의 원두는 국가 안에 여럿 들어갈 수 있으나 원두에는 국가가 하나밖에 들어가지 못한다. 이런 관계는 일대다 관계이다.
이 관계는 자동으로 유추되기도 하나 세부적인 설정을 위해 직접 써줄 수도 있다.
deleteRule은 관계성이 있는 두 모델 중 한 쪽의 데이터가 삭제 될 경우에 대한 규칙을 정하는 파라미터로 기본값은 .nullfy이다. 기본값의 경우엔 데이터 삭제로 생기는 공백에 nul값이 들어간다.
.cascade를 넣게 될 경우 해당 모델에 관련된 데이터를 모두 삭제하게 된다. 예를 들어 콜롬비아에 원두 A, B, C가 있을 때 콜롬비아를 삭제하면 콜롬비아와 원두 A, B, C가 같이 삭제된다.

post-custom-banner

0개의 댓글