⛏️

SwiftDataで@Queryを条件付き取得にする

2025/01/02に公開

SwiftData@Queryを使用してデータを取得する際に単純な全件取得ではなく、条件付きで取得したい場合に#Predicateを使用します。

公式ドキュメント
Predicate | Apple Developer Documentation

全件取得の場合

import SwiftData
import SwiftUI

struct DailyTask {
    let date: Date
}

struct ContentView: View {
    @Query var dailyTasks: [DailyTask]
    
    var body: some View {
        VStack {
            ....
        }
    }
}

条件付き取得

実装例として「dateが今週のもの」を取得する処理を書いてみます

一旦公式ドキュメント通りに書いてみる

// 公式ドキュメントの実装例
let messagePredicate = #Predicate<Message> { message in
    message.length < 100 && message.sender == "Jeremy"
}

// dateが今週のものを取得する 
let pridicateDailyTask = #Predicate<DailyTask> { dailyTask in
    let calendar = Calendar.current
    let now = Date()
    let startOfWeek = Calendar().date(from: Calendar().dateComponents([.yearForWeekOfYear, .weekOfYear], from: now))!
    let endOfWeek = Calendar().date(byAdding: .day, value: 6, to: startOfWeek)!
    return dailyTask.date >= startOfWeek && dailyTask.date <= endOfWeek
}

実際に書いてみると以下のエラーが表示されビルドが通りません。

Instance member 'pridicateDailyTask' cannot be used on type 'ContentView'; did you mean to use a value of this type instead?

解決策

staticをつけると実行できました

import SwiftData
import SwiftUI

struct DailyTask {
    let date: Date
}

struct ContentView: View {
    // staticをつける!!
    static let pridicateDailyTask = #Predicate<DailyTask> { dailyTask in
        let calendar = Calendar.current
        let now = Date()
        let startOfWeek = Calendar().date(from: Calendar().dateComponents([.yearForWeekOfYear, .weekOfYear], from: now))!
        let endOfWeek = Calendar().date(byAdding: .day, value: 6, to: startOfWeek)!
        return dailyTask.date >= startOfWeek && dailyTask.date <= endOfWeek
    }
    
    @Query(filter: pridicateDailyTask) var dailyTasks: [DailyTask]
    
    var body: some View {
        VStack {
            ....
        }
    }
}

functionで書きたい場合

引数で値を受け取りたかったり、可読性の観点からFunctionで書きたい場合はこんな感じです

import SwiftData
import SwiftUI

struct DailyTask {
    let date: Date
}

struct ContentView: View {	
    @Query(filter: pridicateDailyTask()) var dailyTasks: [DailyTask]
    
    var body: some View {
        VStack {
            ....
        }
    }
    
    // staticをつける!!
    static func pridicateDailyTask() -> Predicate<DailyTask> {
      let calendar = Calendar.current
      let now = Date()
      let startOfWeek = Calendar().date(from: Calendar().dateComponents([.yearForWeekOfYear, .weekOfYear], from: now))!
      let endOfWeek = Calendar().date(byAdding: .day, value: 6, to: startOfWeek)!
      return #Predicate<DailyTask>{ dailyTask in
          dailyTask.date >= startOfWeek && dailyTask.date <= endOfWeek
      }
    }
}

最後に

不備、間違い等あればご指摘いただけると幸いです。

参考記事

Swift #Predicate with function usage on the argument

Discussion