0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftDataの読み出し関数が必要な場合と不必要な場合の使い分け。

Posted at

自分の書いてた元コード

//
//  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() は不要になる理由

  1. @QuerySwiftData の変更をリアルタイムで検知 し、リストを自動更新する。
  2. onAppear などで fetchLookedUsers() を呼び出す必要がない。
  3. データベースの状態と UI の同期が常に維持される。

→ そのため、単純な「データの一覧表示」なら fetchLookedUsers() は不要。


🔍 fetchLookedUsers() を使うべきケース

ただし、以下のようなケースでは fetchLookedUsers() が役立ちます。

✅ 1. @Query を使えない ViewModel や非 SwiftUI 環境

  • @QuerySwiftUI の View でしか使えない
  • ObservableObject で状態を管理する ViewModelAPI コールと組み合わせたい場合 は、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 経由でデータ取得
        }
    }
}

🎯 これが必要な理由

  • @QuerySwiftUI でしか使えない → 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 でデータ管理 必要 @QueryView でしか使えないため
APIデータと統合したい 必要 @Query だけでは API からのデータ取得を管理できない
特定のフィルタや並び替えを適用したい 必要 @Query はシンプルなフィルタしかできない
大量データの管理(ページング) 必要 @Query はすべてのデータをロードするため

👀 結論

🚀 fetchLookedUsers() は「単純なリスト表示なら不要」だが、「データ管理・フィルタ適用・大量データの最適化が必要なら使うべき」!

使わなくてもいい場面

  • SearchUserView のように 単純なリスト表示 なら、@Query だけでOK

必要な場面

  • ViewModel でデータを管理したい
  • API データと組み合わせる
  • フィルタや並び替えを適用したい
  • 大量データをページング処理したい

このプロジェクトの用途次第で、@Query だけで十分なのか、fetchLookedUsers() を活用するべきかを判断すると良い でしょう! 🚀

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?