SwiftDataP
概要
txt
Application <---> Model Context
|
Model Container <---> Database
Model
swift
@Model
class Book: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
@Relationship(deleteRule: .nullify, inverse: \Author.books) var author: Author?
..
init() {
}
}
swift
@Model
class Author: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
@Relationship(deleteRule: .nullify) var books: [Book]? = []
..
}
- TODO: inverse つける意味は?
App
swift
@main
struct TestApp: App {
WindowGroup {
ContentView()
.environment(appData)
.modelContainer(for: [Book.self])
}
}
ContentView
固定のQuery
swift
struct ContentView: View {
@Environment(ApplicationData.self) private var appData
@Environment(\.modelContext) var modelContext
@Query var listBooks: [Book]
}
Dynamic Query
swift
struct ContentView: View {
@Environment(ApplicationData.self) private var appData
@Environment(\.modelContext) var modelContext
@Query var listBooks: [Book]
func init() { // TODO: init 以外でも行ける?かを確認
_listBooks = Query(sort: \Book.title, order: ..)
}
}
Preview での書き方
swift
#Preview {
ContentView()
.environment(ApplicationData())
.modelContainer(for: [Book.self], inMemory: true)
@Query : View から参照する場合はこれ
Sort
swift
// 1個の場合
@Query(sort: \Book.title, order: .forward) private var listBooks: [Book]
// 複数の場合
@Query(sort: [
SortDescriptor(\Book.author?.name, order: .forward),
SortDescriptor(\Book.year),
]) private var listBooks: [Book]
@Query(sort: [
SortDescriptor(\Book.title, comparator: .lexical, order: .forward)
]) private var listBooks: [Book]
.lexical
: 文字通りの辞書順?.localizedStandard
: Finder で使われている
Filter
swift
@Query(filter: #Predicate<Book> {
$0.year == 1986
}) private var listBooks: [Book]
@Query(filter: #Predicate<Book> {
$0.author?.name.localicaseStandardContains("Stephen") == true
}) private var listBooks: [Book]
Fetch : View以外から参照する場合はこれ
でも、 @Query
の場合と異なり、変更の監視はされないということだと思うので、使い分けが必要そう
swift
// Fetch
let predicate = #Predicate<Book> { book in
book.author?.name == nil
}
let sd = SortDescriptor<Book>(\.title, order: .forward)
let fd = FetchDescriptor<Book>(predicate: predicate, sortBy: [sd])
// List 取得の場合
if let list = try? modelContext.fetch(fd) {
for book in list {
print(book.title)
}
}
// count だけの場合
if let count = try? modelContext.fetchCount(descriptor) {
print(count)
}
IDで探す
これが最適かは不明だが
swift
@State var selectedBookID: PersistentIdentifier? = nil
テンプレ
swift
struct XxxApp: App {
// @StateObject var appData = ApplicationData()
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Park.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()
}
// .environmentObject(appData)
.modelContainer(sharedModelContainer)
}
}
Group by ..
SQL 相当のものをそのまま叩けるわけでもなさそうなので、 全部取ってきてから Dictionary で group 化する。count をひたすらするより速い。
swift
let parksByPref = Dictionary(grouping: parks, by: { $0.pref })
その他
- 配列は使用できないらしい
基本型以外のもの
Codable にすれば良いらしい
SwiftDataの@Modelを付けたクラスにStructやEnumを含めたい
iCloud 同期されないとき
- cascade は対応してなさそうなので、nullify にしておく
- 型は すべてのフィールド optional にする。初期値もセットする必要がある