自分の書いてた元コード
//
// SwiftDataService.swift
// Rudux-SwiftUI
//
//
//
import SwiftData
import Foundation
import SwiftUICore
// SwiftDataはメインスレッドで操作推奨外すとWarning
@MainActor
protocol SwiftDataServiceProtocol {
func fetchLookedUsers() throws -> [LookedUser]
func saveUser(id: String?, name: String?, profileImageUrl: String?, followeesCount: Int?, followersCount: Int?) throws
func deleteUser(user: LookedUser, completion: @escaping (Result<Void, Error>) -> Void)
}
class SwiftDataService: SwiftDataServiceProtocol {
private let modelContext: ModelContext
init(modelContext: ModelContext) {
self.modelContext = modelContext
}
// データを取得
func fetchLookedUsers() throws ->[LookedUser] {
let fetchDescriptor = FetchDescriptor<LookedUser>()
do {
let users = try modelContext.fetch(fetchDescriptor)
return users
} catch {
print("Error fetching items: \(error)")
throw Errors.swiftDataReadError
}
}
// データを保存
func saveUser(id: String?,
name: String?,
profileImageUrl: String?,
followeesCount: Int?,
followersCount: Int?) throws {
guard let id = id else {
print("ID cannot be nil")
return
}
do {
// 既存データを取得または新規作成
// matchingでアンラップしてはいけない(これ起因でエラーに落ちてた)
let user = try modelContext.findOrInsert(
matching: #Predicate<LookedUser> { $0.id == id },
create: LookedUser(id: id,
name: name,
profileImageUrl: profileImageUrl,
followeesCount: followeesCount,
followersCount: followersCount)
)
// プロパティの更新
user.id = id
user.name = name
user.profileImageUrl = profileImageUrl
user.followeesCount = followeesCount
user.followersCount = followersCount
// 保存処理
try modelContext.save()
try SharedModelContainer.shared.saveContext() // 通知をトリガー
} catch {
throw Errors.swiftDataWriteError
}
}
// データを削除
func deleteUser(user: LookedUser, completion: @escaping (Result<Void, Error>) -> Void) {
do {
modelContext.delete(user)
try modelContext.save()
completion(.success(()))
} catch {
print("Error deleting item: \(error)")
completion(.failure(Errors.swiftDataDeleteError))
}
}
}
チャットGPTへの質問
読み出し関数と@Queryの使い分け。
結論: fetchLookedUsers()
は基本的に不要だが、使うケースはある
SwiftData の @Query
を使えば リストデータの読み出しは自動で行われる ため、
基本的には fetchLookedUsers()
のような手動のデータ取得メソッドは不要 です。
しかし、fetchLookedUsers()
が役立つケースもある ため、以下に詳しく解説します。
🚀 fetchLookedUsers()
が不要なケース
✅ @Query
を使えば View で自動的にデータを取得 & 更新できる
struct SearchUserView: View {
@Query private var lookedUsers: [LookedUser] // これだけでOK
var body: some View {
List(lookedUsers, id: \.id) { user in
Text(user.name ?? "No Name")
}
}
}
🎯 これで fetchLookedUsers()
は不要になる理由
-
@Query
は SwiftData の変更をリアルタイムで検知 し、リストを自動更新する。 -
onAppear
などでfetchLookedUsers()
を呼び出す必要がない。 - データベースの状態と UI の同期が常に維持される。
→ そのため、単純な「データの一覧表示」なら fetchLookedUsers()
は不要。
🔍 fetchLookedUsers()
を使うべきケース
ただし、以下のようなケースでは fetchLookedUsers()
が役立ちます。
✅ 1. @Query
を使えない ViewModel や非 SwiftUI 環境
-
@Query
は SwiftUI のView
でしか使えない。 -
ObservableObject
で状態を管理するViewModel
やAPI コールと組み合わせたい場合
は、fetchLookedUsers()
のようなメソッドが必要。
📌 ViewModel で fetchLookedUsers()
を使う例
@Observable
class SearchUserViewModel {
private let dataService = SwiftDataService(modelContext: SharedModelContainer.shared.context)
@Published var lookedUsers: [LookedUser] = []
func loadUsers() {
do {
self.lookedUsers = try dataService.fetchLookedUsers()
} catch {
print("Failed to load users: \(error)")
}
}
}
Viewで ViewModel
を使う
struct SearchUserView: View {
@StateObject private var viewModel = SearchUserViewModel()
var body: some View {
List(viewModel.lookedUsers, id: \.id) { user in
Text(user.name ?? "No Name")
}
.onAppear {
viewModel.loadUsers() // ViewModel 経由でデータ取得
}
}
}
🎯 これが必要な理由
-
@Query
はSwiftUI
でしか使えない → ViewModel でデータ管理したい場合はfetchLookedUsers()
が必要。 - API 経由でデータを取得する処理と統合したい場合、手動でデータを取得・更新できるメソッドが必要。
✅ 2. @Query
にフィルタや並び替えを適用したい
@Query
には簡単なフィルタしか設定できない。
fetchLookedUsers()
なら、データの取得時にカスタムフィルタや並び替えが可能。
📌 例えば「最近閲覧した順」に並べる場合
func fetchLookedUsersSorted() throws -> [LookedUser] {
var fetchDescriptor = FetchDescriptor<LookedUser>()
fetchDescriptor.sortBy = [SortDescriptor(\.lastViewedDate, order: .reverse)] // 最新順
return try modelContext.fetch(fetchDescriptor)
}
🎯 @Query
ではできない複雑なクエリを実行可能!
✅ 3. パフォーマンス最適化(大量データの読み出し)
-
@Query
は すべてのデータをメモリにロードする ため、大量のデータがあると パフォーマンスが悪化する。 -
fetchLookedUsers()
を使えば、一度に取得するデータ量を制限(ページング)できる。
📌 例えば「最大 50 件まで取得」する
func fetchLookedUsers(limit: Int = 50) throws -> [LookedUser] {
var fetchDescriptor = FetchDescriptor<LookedUser>()
fetchDescriptor.fetchLimit = limit
return try modelContext.fetch(fetchDescriptor)
}
🎯 @Query
ではすべてのデータを取得するため、大量データの管理には手動の fetchLookedUsers()
が適切!
💡 fetchLookedUsers()
はいる? いらない?
ケース | fetchLookedUsers() 必要? |
理由 |
---|---|---|
単純なリスト表示 (List でデータをそのまま表示) |
不要 |
@Query でリアルタイム更新が可能 |
ViewModel でデータ管理 | 必要 |
@Query は View でしか使えないため |
APIデータと統合したい | 必要 |
@Query だけでは API からのデータ取得を管理できない |
特定のフィルタや並び替えを適用したい | 必要 |
@Query はシンプルなフィルタしかできない |
大量データの管理(ページング) | 必要 |
@Query はすべてのデータをロードするため |
👀 結論
🚀 fetchLookedUsers()
は「単純なリスト表示なら不要」だが、「データ管理・フィルタ適用・大量データの最適化が必要なら使うべき」!
✅ 使わなくてもいい場面
-
SearchUserView
のように 単純なリスト表示 なら、@Query
だけでOK。
✅ 必要な場面
- ViewModel でデータを管理したい
- API データと組み合わせる
- フィルタや並び替えを適用したい
- 大量データをページング処理したい
このプロジェクトの用途次第で、@Query
だけで十分なのか、fetchLookedUsers()
を活用するべきかを判断すると良い でしょう! 🚀