Searching within a data model using an array matching multiple terms I have a standard tableview app that displays information from a data model array that uses a custom class. There is a UISearchController to filter the data.

A simplified version of the custom class is shown. I use a computed property for a string that is checked when searching the data.

class myObject: NSObject {

    // Inherits from NSObject as I use NSCoding for persistence

    var id: String = UUID().uuidString // These are unique for each item

    var name: String = ""
    var type: String = ""
    var batch: String = ""

    var searchableString: String {
        // This is a concatenation of some of the properties
        return self.name + " " + self.type + " " + self.batch
    }

    // etc...
}


Every time the user types a new character into the search bar of the UISearchController, the following method is called, which filters the main data array into a filteredItems array. Initially, I used a very basic form where the entire search text had to match.

func searchItems() {
    if let searchText = searchController.searchBar.text {
        filteredItems = allItems.filter { item in
            return item.searchableString.lowercased().contains(searchText.lowercased())
        }
    }
}


filteredItems is then displayed in the tableview.

I wanted to improve this so that each word typed in the search bar is searched for separately. I tried this, but of course it filters using OR logic (any word can match).

func searchItems() {
    if let searchText = searchController.searchBar.text?.split(separator: " ") {
        // Need to convert substring from split back to string
        let searchArray = searchText.map() { substring in
            String(substring)
        }
        filteredItems = allItems.filter { item in
            return searchArray.contains(where: drug.searchableString.lowercased().contains)
        }
    }
}


In order to get the desired AND logic (every word typed must match), I had to use this.

func searchItems() {
    if let searchText = searchController.searchBar.text?.split(separator: " ") {
        // Need to convert substring from split back to string
        let searchArray = searchText.map() { substring in
            String(substring)
        }
        filteredItems = allItems.filter { item in
            for word in searchArray {
                if !item.searchableString.lowercased().contains(word) {
                    return false
                }
            }
            return true
        }
    }
}


This works as intended, but it does not seem elegant. Is there a better way to filter the array allItems to filteredItems based on matching text in those String properties of the objects in my data model?
You have picked a hard data structure and hard algorithm to solve it.  Specifically, you have your searchable list as a list of strings and need to parse and convert it for each search.  You then do linear searches.

If you are going to precompute or cache your search information, consider creating a dictionary where the keys are the words and the values are sets of item indices.  Then you search is anding the set of indices with the value of each word.

Alternately, if you are optimizing for code brevity, use a regular expression. If your search strings were like ':MyName:MyType:MyBatch:' You want to create an expression from your search like :(word1|word2|word3): and use 

NSUInteger numberOfMatches = [regex numberOfMatchesInString:string
                                                    options:0
                                                      range:NSMakeRange(0, [string length])];


and make sure it matches the number of words.   Yes, more work if you want to avoid the, er, inventive user, searching for "MyBatch MyBatch MyBatch".

Keep trying to make it prettier :)