Post

Replies

Boosts

Views

Activity

Reply to How to handle predicate with optionals in SwiftData
Sorry, I wasn't able to test my answer earlier and was confident that this kind of query worked but I end up with the same error: to-many key not allowed here. I tried other alternatives but couldn't find one that worked. Doesn't seem like it's possible to use optional to-many relationships inside a Predicate (even checking for $0.tags != nil or similar will crash) A workaround would be to query on the to-one side of the relationship, i.e Tag and compute the array of Item from the results. It's not ideal but I couldn't find another way. Since it's working for non-optional relationships we can only hope that this will possible in future releases. struct ItemsView: View { @Query private var tags: [Tag] var items: [Item] { self.tags.lazy.compactMap(\.items).flatMap { $0 } } init(searchText: String) { self._tags = Query(filter: #Predicate<Tag> { $0.name.localizedStandardContains(search) }) } }
Dec ’23
Reply to SwiftData query filter on an @State var
You cannot use any instance member inside a Predicate so this shouldn't even compile struct MyView: View { @State private var selectedMonth: String = "" // Instance member 'selectedMonth' cannot be used on type 'MyView'; did you mean to use a value of this type instead? @Query(filter: #Predicate<Transaction> { $0.date.monthString == selectedMonth }) var transactions: [Transaction] } You can achieve what you want by putting your Query into a subview where you can provide a predicate or sort value using dependency injection. struct ContainerView: View { @State private var selectedMonth: String = "" @State private var selectedYear: String = "" var body: some View { TransactionList(month: selectedMonth, year: selectedYear) // called every time your state properties change .toolbar { Menu { ... } // update your states values } } } struct TransactionList: View { @Query private var transactions: [Transaction] init(month: String, year: String) { self._transactions = Query(filter: #Predicate<Transaction> { $0.date.monthString == month && $0.date.yearString == year }, sort: \.date) } } For your information, you can retrieve SwiftData models by using FetchDescriptor. let fetchDescriptor = FetchDescriptor<Transaction>(predicate: #Predicate { ... }, sortBy: [SortDescriptor(\.date)]) do { let transactions = try modelContext.fetch(fechDescriptor) } catch { } You can find more examples in the official documentation like Preserving your app’s model data across launches
Dec ’23
Reply to SwiftData Predicate not working.
It's because you force unwrap your properties inside the predicate. It works fine if you use flatMap to access the unwrapped date value and define your searched strings outside of the predicate scope let searchCategory = "Income" let searchType = "Business" #Predicate<Transaction> { $0.date.flatMap { $0 >= startDate && $0 <= endDate } == true && $0.category == searchCategory && $0.ttype == searchType }
Dec ’23
Reply to Trying to connect model in SwiftData with SwiftUI TextField
It's a little unclear without code example but I'm guessing your have something like that var person: Person // SwiftData model retrieved from a @Query @State private var text: String = "0" TextField(person.name, text: self.$text) This will show "0" since the text field display the content of the value it is binded to. The first argument is used as a placeholder to describe the purpose of this field to the user (and is displayed only when the text is an empty string which is not the case here). You have 2 options Use a State property for the textfield value that you update when the view appears. It will no update your SwiftData model when the value changes so you will need to implement a validation/save method struct PersonForm: View { var person: Person @State private var text: String = "0" var body: some View { VStack { Button("Save") { self.person.name = text } Text("Name", text: self.$text) } .onAppear { self.text = self.person.name } } } Bind the SwiftData model property to the text field. This will update your model directly according to the user input without needing validation. IMHO, this is not the best solution since It can trigger a lot of database updates (even if SwiftData batches them) and also view updates if you reference your model in another view, SwiftUI will ask for redraw at each change the user make inside the text field struct PersonForm: View { @Bindable var person: Person @State private var text: String = "0" var body: some View { Text("Name", text: self.$person.name) } } Hope this helps
Dec ’23
Reply to Query SwiftData model for max value of an attribute
You can provide a SortDescriptor and a fetchLimit to achieve this. If we take the example of @DelawareMathGuy with a Location object that have a position property, you could do something like that var fetchDescriptor = FetchDescriptor<Location>(sortBy: [ SortDescriptor(\.position, order: .reverse) // sort the object by descending position ]) fetchDescriptor.fetchLimit = 1 // retrieve only one object let locations = try? modelContext.fetch(fetchDescriptor) let maxPositionLocation = locations?.first As stated by @DelawareMathGuy, derived attributes are not yet supported by SwiftData. You could create your own by defining computed properties or by using the @Transient attributes but this will not be as efficient (I advise reading Paul Hudson from 'hackingwithswift' article about this)
Dec ’23
Reply to `.chartAngleSelection` doesn't register taps when tapping a `SectorMark`, have to hold down a little bit
Swift Charts allows us to provide a custom gesture for selection by using the chartGesture(_:) modifier. You can for exemple configure a SpatialTapGesture to get the tap location and update the chart angle value by calling the selectAngleValue(at:) method. By using the below code, I managed to get rid of the "hold down" behaviour. .chartGesture { chart in SpatialTapGesture() .onEnded { event in let angle = chart.angle(at: event.location) chart.selectAngleValue(at: angle) } }
Dec ’23