Nested subqueries built with NSExpression crashes but not its predicate
I have some code that can recursively build nested subqueries with NSComparisonPredicate
and NSExpression
. Here it is when it's flat:
// Nested subquery
let entityPath = NSExpression(forKeyPath: "$s.mainUserResult.operations")
let subQueryExpression = NSExpression(forSubquery: entityPath, usingIteratorVariable: "t", predicate: NSPredicate(format: "$t.operationType == 3"))
let countSubQuery = NSExpression(format: "%@.@count", argumentArray: [subQueryExpression])
let p1 = NSComparisonPredicate(leftExpression: countSubQuery,
rightExpression: NSExpression(forConstantValue: 0),
modifier: .direct,
type: .greaterThan,
options: NSComparisonPredicate.Options(rawValue: 0))
// Main subquery
let entityPath2 = NSExpression(forKeyPath: "sessions")
let subQueryExpression2 = NSExpression(forSubquery: entityPath2, usingIteratorVariable: "s", predicate: p1)
let countSubQuery2 = NSExpression(format: "%@.@count", argumentArray: [subQueryExpression2])
let p2 = NSComparisonPredicate(leftExpression: countSubQuery2,
rightExpression: NSExpression(forConstantValue: 0),
modifier: .direct,
type: .greaterThan,
options: NSComparisonPredicate.Options(rawValue: 0))
The above code crashes when I fetch some entities with the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't have a non-relationship collection element in a subquerySUBQUERY($s.mainUserResult.operations, $t, $t.operationType == 3)'
What's really weird is that when I build a predicate by hand by copying the resulted predicateFormat
of the NSComparisonPredicate, it actually works!
Fetching with the predicate p2
results in an error, whereas fetching with the resulted format works:
let p2 = NSPredicate(format: "SUBQUERY(sessions, $s, SUBQUERY($s.mainUserResult.operations, $t, $t.operationType == 3).@count > 0).@count > 0")
How to make the NSComparisonPredicate
work?
1 answer
-
answered 2022-05-04 14:59
Willeke
NSExpression(forKeyPath: "$s.mainUserResult.operations")
does
self.value(forKeyPath: "$s.mainUserResult.operations")
This should be
s.value(forKeyPath: "mainUserResult.operations")
expression:
NSExpression(format: "$s.mainUserResult.operations", argumentArray: [])
do you know?
how many words do you know
See also questions close to this topic
-
SwiftUI, apply drawGesture only when over Image
I'm try to apply the gesture only when the user is over the Image display not when tapped outside the image.
Any suggestion how I can do? this following code draw also when user tap outside the image.
struct ContentView: View { @StateObject var am = AppManager() @State var switchToTakePicture = false @State var paths: [PathContainer] = [] @State var currentDraggingId = UUID() @State var spikoPic = #imageLiteral(resourceName: "spiko-16.jpeg") @State var centerOFimage = CGSize(width: 0, height: 0) var body: some View { GeometryReader { proxy in ZStack { Image(uiImage: spikoPic) .resizable() .scaledToFit() .position(x: proxy.size.width/2, y: proxy.size.height/2) .background(GeometryReader { Color.clear.preference(key: ViewRectKey.self, value: [$0.frame(in: .global)]) }) .gesture(drawGesture) // not correct ForEach(paths) { container in // draw and set the foreground color of the paths to red container.path .stroke(Color.red, lineWidth: 4) } } .onPreferenceChange(ViewRectKey.self) { rects in print(rects.first ?? .zero) } } } var drawGesture: some Gesture { DragGesture(minimumDistance: 0) .onChanged { value in // The point that the gesture started from let start = value.startLocation // The point that the gesture ended to let end = value.location // the properties of the rectangle to be drawn let rectangle: CGRect = .init(origin: end, size: .init(width: start.x - end.x, height: start.y - end.y)) // create a path for the rectangle let path: Path = .init { path in path.addRect(rectangle) } print("rettangolo = \(rectangle) orig \(rectangle.origin) - height \(rectangle.height) width = \(rectangle.width)") // remove the previous rectangle that was drawen in current // process of drawing paths.removeAll { $0.id == currentDraggingId } // append the new rectangle paths.append(.init(id: currentDraggingId, path: path)) } .onEnded { _ in // renew the dragging id so the app know that the next // drag gesture is drawing a completely new rectangle, // and is not continuing the drawing of the last rectangle currentDraggingId = .init() } } }
i want no box outside
-
How to navigate between NavigationLink while leave a part of main window stay the same in SwiftUI
I would like to navigate between different
NavigationLink
s inNavigationView
while some part of the main window stay the same. For example, I want to make a music app and I want to let the play controller always on top, while I can display different navigation contents (songs page, artists page...) using the rest of the window.Like what's showed in the picture below, I want to keep the red part always there while the blue part changes.
My code would be like below, but it won't work correctly. The
AlwaysStayView()
disappears when I click anyNavigationLink
on sidebar. So, how can I correct it or is there any solution (prefer in SwiftUI, but framework like UIKit would also be OK). I would appreciate it.NavigationView { List { NavigationLink { DiscoverView() } label: { Label("Discover", systemImage: "magnifyingglass") } NavigationLink { SongsView() } label: { Label("Songs", systemImage: "music.note") } NavigationLink { ArtistsView() } label: { Label("Artists", systemImage: "music.mic") } } } .listStyle(SidebarListStyle()) VStack { AlwaysStayView() SongsView() } }
-
NumberFormatter and unsigned with UInt64.max
I'm trying to create a string representing
UInt64.max
usingNumberFormatter
. Here's the code:let formatter = NumberFormatter() formatter.usesGroupingSeparator = true formatter.numberStyle = .decimal formatter.positiveFormat = "# ##0.#########" formatter.maximumSignificantDigits = 20 formatter.usesSignificantDigits = false formatter.maximumFractionDigits = 20 formatter.minimumFractionDigits = 0 formatter.alwaysShowsDecimalSeparator = false // formatter.roundingMode = .halfUp let text1 = formatter.string(for: NSNumber(value: Int64.max)) let text2 = formatter.string(for: NSNumber(value: UInt64.max)) print(text1) print(text2)
which prints:
Optional("9,223,372,036,854,780,000")
Optional("-1")but should print
Optional("9223372036854775807")
Optional("18,446,744,073,709,551,615")It looks like
NSNumber
is roundingInt64
and isn't taking theUIn64
. The obj-c version ofNSNumberFormatter
works fine.Am I missing something or is there a problem with
NumberFormatter
? -
Dynamically add textfields with a for each loop and a button in swiftUI
I am trying to use a
ForEach
loop to dynamically add textfields inside a form section with a button.Form { ForEach(0..<numberOfItems, id: \.self) { _ in TextField("", text: $listItemEntry) } Button("Add Item") { numberOfItems = numberOfItems + 1 } }
How would I replace the
$listItemEntry
which is currently bound to a state variable so that I can individually type in each text field. As a bonus, what would be the best way to save these entries to CoreData, as I don't really know where to start. -
Core data creates keeps populating with default values and doesn't save on close
I am fairly new to core data. I am trying to make a form where each time I add a section header and text from a sheet view, it appears within a new section. The problem I am having is every time I open the sheet view to enter the section header and text saved via core data, a big list of my default values of List Name and List Section appears on the main screen upon dismissing the sheet view. They also disappear after a relaunch so I don't think I am saving the data correctly.
I currently have the core data entity of
Lists
with the attributes oflistName
andlistSection
which are used in my form as seen in the image.struct ContentView: View { @Environment(\.managedObjectContext) var moc @FetchRequest(sortDescriptors: []) var lists: FetchedResults<Lists> var body: some View { NavigationView { Form() { ForEach(lists) { list in Section(header: Text(list.listSection ?? "List Section")) { NavigationLink { Text("Next Page") } label: { Text(list.listName ?? "List Name") } } } } } }
I create this data via a sheet view. I also use the var dismiss to hide the sheet view, bringing me back to my main view code shown above.
struct AddList: View { @Environment(\.dismiss) var dismiss @State private var listNameEntry: String = "" @State private var listSectionEntry: String = "" @Environment(\.managedObjectContext) var moc @FetchRequest(sortDescriptors: []) var lists: FetchedResults<Lists> var body: some View { let list = Lists(context: moc) //used to save to core data Form { Section() { TextField("List Name", text: $listNameEntry) { list.listName = listNameEntry } TextField("List Section", text: $listSectionEntry) { list.listSection = listSectionEntry } } Section() { Button("Done") { try? moc.save() dismiss() } } } }
-
Core data - TableView - Toggle - Index out of range
I have a data model like this. The data is connected to core data.
import Foundation import CoreData @objc(Aktie) class Aktie: NSManagedObject { @NSManaged var isCompleted: Bool @NSManaged var counter: String? @NSManaged var deletedDate: Date? @NSManaged var id: NSNumber! @NSManaged var notes: String? @NSManaged var title: String? @NSManaged var type: String? @NSManaged var point: String? @NSManaged var saving: String? }
I have made a toggle in my tableview - the relevant code is as follows.
var akties: [Aktie]=[] override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->UITableViewCell { let aktieCell=tableView.dequeueReusableCell(withIdentifier: "aktieCellID", for: indexPath) as! AktieCell let thisAktie: Aktie! thisAktie=nonDeletedNotes()[indexPath.row] aktieCell.aktieTitle.text=thisAktie.title aktieCell.aktieType.text=thisAktie.type aktieCell.AktieCounter.text=thisAktie.counter aktieCell.AktiePoints.text=thisAktie.point aktieCell.accessoryType=thisAktie.isCompleted ? .checkmark: .none override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let complete=UIContextualAction(style: .normal, title: "Completed") { (action, sourceView, completionHandler) in // print("Completed") let cell=tableView.cellForRow(at: indexPath) as! AktieCell self.akties[indexPath.row].isCompleted=(self.akties[indexPath.row].isCompleted) ? false: true cell.accessoryType=(self.akties[indexPath.row].isCompleted) ? .checkmark: .none completionHandler(true) } complete.backgroundColor=.systemGreen complete.image=self.akties[indexPath.row].isCompleted ? UIImage(named: "undo"): UIImage(named: "tick") let swipeConfiguration=UISwipeActionsConfiguration(actions: [complete]) return swipeConfiguration }
The code builds the app without any error but when I try to swipe the row it stops and gives me the following error:
Thread 1: Fatal error: Index out of range.
-
Can someone please explain the query shown in depth?
Question: write a SQL query to find the salespeople from the given tables who had more than one customer. Return
salesman_id
andname
.Sample table:
customer
customer_id cust_name city grade salesman_id 3002 Nick Rimando New York 100 5001 3007 Brad Davis New York 200 5001 3005 Graham Zusi California 200 5002 3008 Julian Green London 300 5002 3004 Fabian Johnson Paris 300 5006 3009 Geoff Cameron Berlin 100 5003 3003 Jozy Altidor Moscow 200 5007 3001 Brad Guzan London 5005 Sample table:
salesman
salesman_id name city commission 5001 James Hoog New York 0.15 5002 Nail Knite Paris 0.13 5005 Pit Alex London 0.11 5006 Mc Lyon Paris 0.14 5007 Paul Adam Rome 0.13 5003 Lauson Hen San Jose 0.12 Solution
SELECT salesman_id,name FROM salesman a WHERE 1 < (SELECT COUNT(*) FROM customer WHERE salesman_id = a.salesman_id);
So basically am unable to understand the subquery how can we compare two table column without using the joins and secondly how this where condition is working as a group by as well and provide the count. So if someone can help me understand this subquery in depth with easy language then it would be really helpful.
This question and solution is correct because am practicing on W3resource and you can find this question under the topic of SQL Subquery Exercises in the exercise 11.
-
Subquery with an Exist
I am a user working in inventory management attempting to return information regarding product name, location, and its availability at the location from the adventure works 2017 database. While trying to run the query, I am getting an error message that states I have too many expressions in my subquery list and that I can only do that if I start the subquery with 'Exists'. I suppose I do not understand what I am doing wrong, maybe someone could explain how 'Exists' works? Is there a way I can rewrite this so I can return both expressions in the subquery? I will include the syntax and error message below.
SELECT Production.Product.Name ,(SELECT Production.Location.Name ,Production.Location.Availability FROM Production.Location WHERE Production.Location.LocationID = Production.ProductInventory.LocationID) FROM Production.Product INNER JOIN Production.ProductInventory ON Production.Product.ProductID = Production.ProductInventory.ProductID;
-
MPMediaPropertyPredicate - filter MPMediaQuery by songs duration
I am fetching music tracks from user's music library like this:
let query = MPMediaQuery.songs() let predicate = MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyIsCloudItem) query.addFilterPredicate(predicate) if let fetchedSongs = query.items { // Do something with tracks }
However, I don't need songs longer than 5 minutes. I tried to add a new predicate
MPMediaPropertyPredicate
to filter out all tracks longer than 5 minutes but with no success - I am not sure what to use in thevalue
parameter and if I can use a condition like"duration < 300"
in a simpleNSPredicate
. -
Value of type [ViewModel] has no member nsPredicate
I have a list of bookmarks stored with Core Data that i want to search through. But i'm getting this error next to
vm.myBookmarks.nsPredicate = NSPredicate(value: true)
in 'var query' below.Value of type '[MyBookmarkViewModel]' has no member 'nsPredicate'
@ObservedObject var vm : MyBookmarksViewModel @State private var searchText = "" ForEach(vm.myBookmarks) { myBookmark in Text(myBookmark.name) Text(myBookmark.url) } .searchable(text: query) var query: Binding<String> { Binding { searchText } set: { newValue in searchText = newValue if newValue.isEmpty { vm.myBookmarks.nsPredicate = NSPredicate(value: true) } else { vm.myBookmarks.nsPredicate = NSPredicate(format: "name CONTAINS[cd] %@", newValue) } } }
MyBookmarksViewModel
class MyBookmarksViewModel: NSObject, ObservableObject { @Published var myBookmarks = [MyBookmarkViewModel]() private let fetchedResultsController: NSFetchedResultsController<MyBookmark> private var context: NSManagedObjectContext init(context: NSManagedObjectContext){ self.context = context self.fetchedResultsController = NSFetchedResultsController(fetchRequest: MyBookmark.all , managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) super.init() fetchedResultsController.delegate = self fetchAll() } private func fetchAll() { do { try fetchedResultsController.performFetch() guard let myBookmarks = fetchedResultsController.fetchedObjects else { return } self.myBookmarks = myBookmarks.map(MyBookmarkViewModel.init) } catch { print(error) } } } extension MyBookmarksViewModel: NSFetchedResultsControllerDelegate { func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { guard let myBookmarks = controller.fetchedObjects as? [MyBookmark] else { return } self.myBookmarks = myBookmarks.map(MyBookmarkViewModel.init) } }
MyBookmarkViewModel
struct MyBookmarkViewModel: Identifiable, Hashable{ init(myBM: MyBookmark) { self.myBM = myBM } private let myBM: MyBookmark var id: NSManagedObjectID { myBM.objectID } var name: String { myBM.name ?? "" } }
MyBookmark
@objc(MyBookmark) public class MyBookmark: NSManagedObject, BaseModel { static var all: NSFetchRequest<MyBookmark> { let request: NSFetchRequest<MyBookmark> = MyBookmark.fetchRequest() request.sortDescriptors = [] return request } } extension MyBookmark { @nonobjc public class func fetchRequest() -> NSFetchRequest<MyBookmark> { return NSFetchRequest<MyBookmark>(entityName: "MyBookmark") } @NSManaged public var name: String? @NSManaged public var url: String? } extension MyBookmark : Identifiable { }
This is for a macOS app.
-
Building a conditional expression using the Mapbox iOS SDK
I'm updating an existing iOS app that uses the Mapbox v6 SDK. The app defines a shape source using an array of point features and specifies that the points should be clustered based on a particular radius. Here's the code to do this:
let source = MGLShapeSource(identifier: sourceIdentifier, features: features, options: [.clustered: true, .clusterRadius: 50.0, .maximumZoomLevelForClustering: 14.0])
I also have a circle style layer defined to show the clusters on the map:
let circularClusterLayer = MGLCircleStyleLayer(identifier: "clusteredMarkers", source: source) circularClusterLayer.predicate = NSPredicate(format: "cluster == YES")
This is working as expected but now I need to define the circle color based on an attribute of all the features located in the cluster. Specifically, each of my features has a boolean property in its attributes dictionary called
.needsHelp
. I'm trying to define the circle color for the cluster marker based on this logic:if any of the features in the cluster have .needsHelp == true { // set the circle color = UIColor.red circularClusterLayer.circleColor = NSExpression(forConstantValue: UIColor.red) } else { // set the circle color = UIColor.gray circularClusterLayer.circleColor = NSExpression(forConstantValue: UIColor.gray) }
I'm pretty sure that I should be able to build an expression that returns true if any of the features in the cluster have
.needsHelp == true
or otherwise, returns false. And I believe this expression would be assigned to the .clusterProperties option for the source like this:let expression1 = NSExpression(mglJSONObject: ["any", ["==", ["boolean", ["get", "isInEmergency"]], true]]) let expression2 = NSExpression(forConstantValue: UIColor.red) let clusterPropertyDictionary: NSDictionary = ["clusterContainsFeatureThatNeedsHelp" : [expression1, expression2]] let selectedContactsSource = MGLShapeSource(identifier: sourceIdentifier, features: features, options: [.clustered: true, .clusterRadius: 50.0, .maximumZoomLevelForClustering: 14.0, .clusterProperties: clusterPropertyDictionary])
I've been looking at the Mapbox expressions guide and I think I should be using the "any" decision expression to get a boolean value back indicating whether any of the features have
.needsHelp == true
or not. But so far I've been unable to translate the LISP-like expression syntax into an NSExpression that works.I'm assuming that in addition to the expression above, I will also need another expression to check the value of "clusterContainsFeatureThatNeedsHelp" and then set the circle color accordingly. My best guess is that that would look something like this:
// TODO: check value of clusterContainsFeatureThatNeedsHelp to determine circle color circularClusterLayer.circleColor = NSExpression(forConditional: NSPredicate(format: "%K == %@", "clusterContainsFeatureThatNeedsHelp", NSNumber(value: true)), trueExpression: NSExpression(forConstantValue: UIColor.red), falseExpression: NSExpression(forConstantValue: UIColor.gray))
Unfortunately, I'm not having any luck building either of these NSExpressions successfully. Has anyone done something like this before? If so, I'd appreciate any tips or suggestions!
-
CoreData Fetch: Getting the objects with the maximum of one atttribute and having another attribute in common
I have a CoreData ManagedObject type Event with the properties name:Sting and date:Date.
I want to fetch all EventObject, with the name containing a filter. If more than one object with the same name matches the filter, only the object with the latest date should be returned.
Just to clarify what I want. In a table based approach I would query in SQL like:
SELECT name, max(date) FROM Event WHERE name contains 'filter' GROUP BY name
In CoreData I tried like this, but got:
Could not cast value of type 'NSKnownKeysDictionary1' to 'xx12.Event'.let fetchRequest : NSFetchRequest<Event> = Event.fetchRequest( fetchRequest.predicate = NSPredicate (format: "name contains %@", filter) fetchRequest.sortDescriptors = [NSSortDescriptor (key: "name", ascending: true)] // We want Event not Dictionary back // fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType let keypathExpression = NSExpression(forKeyPath: "date") let maxExpression = NSExpression(forFunction: "max:", arguments: [keypathExpression]) let key = "maxDate" let expressionDescription = NSExpressionDescription() expressionDescription.name = key expressionDescription.expression = maxExpression expressionDescription.expressionResultType = .dateAttributeType fetchRequest.propertiesToFetch = [expressionDescription] let resultEvents = try moc.fetch(fetchRequest)
Example:
if I have these objectsmeeting1, 1.1.2020 meeting1, 1.4.2020 meeting2, 1.1.2020 meeting2, 1.4.2020 party, 2.3.2020
and the filter: meeting
I want to get the objects:meeting1, 1.4.2020 meeting2, 1.4.2020