#Predicate needs better validation

Hi,

Overview

  • I am finding #Predicate to be a bit tricky when used with Swift Data
  • It compiles fine but crashes at runtime
  • I know the fix for the problem just wondering if such pitfalls can be avoided at compile time

Exception

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'can't use NULL on left hand side'
terminating due to uncaught exception of type NSException

CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x11209b000> , can't use NULL on left hand side with userInfo of (null)

Questions

  • Could anything be done to improve the safety to avoid such issues at runtime?
  • Could I write the code better (better than the fix below) to avoid this?

My thoughts

  • Fix is possible however it wasn't obvious to me that there was a problem with my original code
  • Would be nice to prevent them at compile time if possible.
  • Currently got to be really careful to avoid such crashes.

Code

import Foundation
import SwiftData

@Model
class Car {
    var name: String
    var modelRawValue: String?
    
    init(name: String, modelRawValue: String?) {
        self.name = name
        self.modelRawValue = modelRawValue
    }
}

enum CarModel: String, CaseIterable {
    case modelA
    case modelB
}


func makePredicate(filterModels: [CarModel]?) -> Predicate<Car> {
    let filterRawValues = filterModels?.map { $0.rawValue }
    
    let predicate = #Predicate<Car> { car in
        if let filterRawValues {
            if let carModelRawValue = car.modelRawValue {
                filterRawValues.contains(carModelRawValue)
            } else {
                false
            }
        } else {
            true
        }
    }
    
    return predicate
}

func fetch(context: ModelContext) throws {
    let predicate = makePredicate(filterModels: nil)
    let fetchDescriptor = FetchDescriptor(predicate: predicate)
    
    do {
        let cars = try context.fetch(fetchDescriptor)
        print(cars.count)
    } catch {
        print("Error: \(error)")
        throw error
    }
}

Fix

func makePredicate(filterModels: [CarModel]?) -> Predicate<Car> {
    // Checking nil condition even before creating the predicate fixes the issue
    guard let filterModels else { return .true }

    let filterRawValues = filterModels.map { $0.rawValue }
    
    let predicate = #Predicate<Car> { car in
        if let carModelRawValue = car.modelRawValue {
            filterRawValues.contains(carModelRawValue)
        } else {
            false
        }
    }
    
    return predicate
}
#Predicate needs better validation
 
 
Q