swiftswiftuicloudkitswiftdata

SwiftData/CloudKit - Predicate does not support keypaths with multiple components


today I encountered a strange error that’s blocking my progress. I wanted to add CloudKit support to an app that’s already using SwiftData for saving data. I read that 'All properties must either have default values or be marked as optional, alongside their initializer.' Unfortunately, after making those changes, I’m getting the error mentioned in the post title because I’m using a predicate to filter a list. Does anyone know how to work around this, and is this a current limitation of SwiftData?

Step to reproduce:

Example:

import SwiftUI
import SwiftData

@Model
final class Example {
    var category: String!
    var subcategory: String!
    
    init(category: String, subcategory: String) {
        self.category = category
        self.subcategory = subcategory
    }
}

struct ContentView: View {
    @Query private var examples: [Example]
    
    init() {
        let predicate = #Predicate<Example> { example in
            example.category.contains("") || example.subcategory.contains("")
        }
        _examples = Query(filter: predicate)
    }
    
    var body: some View {
        Text("Hello world")
    }
}

@main
struct TestExampleApp: App {
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: Example.self)
        }
    }
}

If I remove the ! from the Example class, everything works fine, but in doing so, I lose the requirement for CloudKit compatibility.


Solution

  • When creating a SwiftData model type you should declare your properties as optional

    var category: String?
    var subcategory: String?
    

    and then you need to unwrapp them in your predicate

    let predicate = #Predicate<Example> { example in
        if let category = example.category, let subcategory = example.subcategory {
            return category.contains("5") || subcategory.contains("b")
        } else {
            return false
        }
    }