NSPredicate format for date comparison

I have the following code to search a tableView. The contains format works fine for string arguments however can't seem to find the correct format to work with date arguments.

  @objc func invoiceSearch(sender:NSSearchField) {
    //    print ("\(#function): \(sender.stringValue)")
            let searchString = sender.stringValue
            var predicate:NSPredicate = NSPredicate()
            if searchString.isEmpty {
                invoices = self.backUpInvoices
            }
            else{       // search field contains data
                if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "All" {
                    predicate = NSPredicate(format: "invoiceNumber contains %@ OR invoiceCustName contains %@ OR invoiceStatus contains %@ OR invoiceDateCreated >= %@",searchString,searchString,searchString,searchString)
                }
                else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Number" {
                    predicate = NSPredicate(format: "invoiceNumber contains %@",searchString)
                }
                else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Customer"{
                    predicate = NSPredicate(format: "invoiceCustName contains %@",searchString)
                }
                else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Status"{
                    predicate = NSPredicate(format: "invoiceStatus contains %@",searchString)
                }
                else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "Invoice Date"{
                      predicate = NSPredicate(format: "invoiceDateCreated >= %@", searchString as CVarArg)
                }
                invoices = (self.backUpInvoices as NSArray).filtered(using: predicate) as! [Invoice]
            }
            invoiceCount = "Found \(items.count ) Invoices"
            invoiceStatusLabel.stringValue = invoiceCount
            self.tableView.reloadData()
    }

InvoiceDateCreated is defined by the following within a NSObject class:
@objc   var invoiceDateCreated: Date? = Date()

The error I get when trying to search on date is 2022-09-26 09:14:36.638553-0500 CashToMe[9107:6838576] [General] -[NSTaggedPointerString objCType]: unrecognized selector sent to instance

I have read through the following documentation Apple Doc for NSPredicate

How can I fix this. Also, if there is a better/newer way to code this I am open to changing the code.

Thanks

Answered by BigEagle in 730834022

I figured this out. I had to create a date from the search string to use in the predicate>>>

Here is the working code:

    @objc func invoiceSearch(sender:NSSearchField) {        
        let searchString = sender.stringValue
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "YYYYMMdd"
        var dateTemplate: String = "20000101"
        var dateSearch: String = ""
        var dateEndSearch: String = ""
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        var predicate:NSPredicate = NSPredicate()
        if searchString.isEmpty {
            invoices = self.backUpInvoices
        }
        else{       // search field contains data
            if searchString.isContainsLetters == false {     // If false must be a date search (Numerics or -)

                dateSearch = dateTemplate                    // Code to handle a begin and end date in
                if searchString.count < 9 {                  // the format YYYYMMDD-YYYYMMDD initially end date is
                    dateSearch.removeFirst(searchString.count) // set to current date
                    dateSearch = searchString + dateSearch
                    searchBeginDate = dateFormatter.date(from: dateSearch)!
                }
                else {
                    if searchString.count > 9 {
                        dateSearch.removeFirst(searchString.count-9)
                        dateEndSearch = searchString
                        dateEndSearch.removeFirst(9)
                        dateSearch = dateEndSearch + dateSearch
                        searchEndDate = dateFormatter.date(from: dateSearch)!
                    }
                }
            }
            if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "All" {
                predicate = NSPredicate(format: "invoiceNumber contains %@ OR invoiceCustName contains %@ OR invoiceStatus contains %@ OR invoiceDateCreated >= %@ AND invoiceDateCreated < %@",searchString,searchString,searchString,searchBeginDate as NSDate, searchEndDate as NSDate)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Number" {
                predicate = NSPredicate(format: "invoiceNumber contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Customer"{
                predicate = NSPredicate(format: "invoiceCustName contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Status"{
                predicate = NSPredicate(format: "invoiceStatus contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "Invoice Date"{
                predicate = NSPredicate(format: "invoiceDateCreated >= %@ AND invoiceDateCreated < %@", searchBeginDate as NSDate, searchEndDate as NSDate)
            }
            invoices = (self.backUpInvoices as NSArray).filtered(using: predicate) as! [Invoice]
        }


Which line causes the error?

Also, is this meant to say items?

invoiceCount = "Found \(items.count ) Invoices"

Either of the following two lines can cause the error:

 predicate = NSPredicate(format: "invoiceNumber contains %@ OR invoiceCustName contains %@ OR invoiceStatus contains %@ OR invoiceDateCreated >= %@",searchString,searchString,searchString,searchString)

 predicate = NSPredicate(format: "invoiceDateCreated >= %@", searchString as CVarArg)

The problem I am having is with the operator >=, I not sure how to filter my table with the date type. The CONTAINS operator only works with strings. I want to be able to filter on a date type variable.

Also, is this meant to say items? invoiceCount = "Found (items.count ) Invoices" ... No, it should say invoices - good catch

The problem seems to be with the variable invoiceDateCreated because it is a foundation date (@objc) and not NSDate object. Any ideas how to change this to get it working?

Accepted Answer

I figured this out. I had to create a date from the search string to use in the predicate>>>

Here is the working code:

    @objc func invoiceSearch(sender:NSSearchField) {        
        let searchString = sender.stringValue
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "YYYYMMdd"
        var dateTemplate: String = "20000101"
        var dateSearch: String = ""
        var dateEndSearch: String = ""
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        var predicate:NSPredicate = NSPredicate()
        if searchString.isEmpty {
            invoices = self.backUpInvoices
        }
        else{       // search field contains data
            if searchString.isContainsLetters == false {     // If false must be a date search (Numerics or -)

                dateSearch = dateTemplate                    // Code to handle a begin and end date in
                if searchString.count < 9 {                  // the format YYYYMMDD-YYYYMMDD initially end date is
                    dateSearch.removeFirst(searchString.count) // set to current date
                    dateSearch = searchString + dateSearch
                    searchBeginDate = dateFormatter.date(from: dateSearch)!
                }
                else {
                    if searchString.count > 9 {
                        dateSearch.removeFirst(searchString.count-9)
                        dateEndSearch = searchString
                        dateEndSearch.removeFirst(9)
                        dateSearch = dateEndSearch + dateSearch
                        searchEndDate = dateFormatter.date(from: dateSearch)!
                    }
                }
            }
            if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "All" {
                predicate = NSPredicate(format: "invoiceNumber contains %@ OR invoiceCustName contains %@ OR invoiceStatus contains %@ OR invoiceDateCreated >= %@ AND invoiceDateCreated < %@",searchString,searchString,searchString,searchBeginDate as NSDate, searchEndDate as NSDate)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Number" {
                predicate = NSPredicate(format: "invoiceNumber contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Customer"{
                predicate = NSPredicate(format: "invoiceCustName contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "invoice Status"{
                predicate = NSPredicate(format: "invoiceStatus contains %@",searchString)
            }
            else if (invoiceSearchField.cell as? NSSearchFieldCell)?.placeholderString == "Invoice Date"{
                predicate = NSPredicate(format: "invoiceDateCreated >= %@ AND invoiceDateCreated < %@", searchBeginDate as NSDate, searchEndDate as NSDate)
            }
            invoices = (self.backUpInvoices as NSArray).filtered(using: predicate) as! [Invoice]
        }


NSPredicate format for date comparison
 
 
Q