Muhammad Umar
Muhammad Umar

Reputation: 11782

Passing multiple closures in swift function and perform filtering

In my code, I have a protocol function search. I want to pass multiple closures that can perform filtering process.

There are few things I am confused. Firstly how to send a generic array of closures into search function. Secondly how will I iterate over each closure to perform the filtering process of each array element.

I have a function

func search(query: String, mainDataList:[T], ____ closuersArray, completionHandler: @escaping ([T]?, Error?) -> Void)  
{
    let queue = DispatchQueue(label: "SomeQueue", attributes: .concurrent)
    queue.asyncAfter(deadline: .now() + 0.3, execute: {
                
            var dataList : [T] = []
            // Perform filtering
     
            for clouser in closuersArray {
                 // Do something here.
            }

            DispatchQueue.main.async {
                completionHandler(dataList, nil)
            }
        })
        
        
}

A couple of random functions as closures are defined as

    func containsDataClosuer(query: String, model: T) -> Bool {
       // Do something here
       return false
    }
    
    func someOtherClouser(query: String, model: T) -> Bool {
       // Do something here
       return false
}

Upvotes: 0

Views: 690

Answers (1)

Larme
Larme

Reputation: 26066

A possible solution:

Initial base:

protocol Searchable {
    var titleField: String { get }
    var subtitleField: String { get }
    var additionalField: String { get }
}

struct Item: CustomStringConvertible {
    var title: String
    var subtitle: String
    var additional: String

    var description: String {
        return "<Item>: T: \(title) - S: \(subtitle) - A: \(additional)"
    }
}
extension Item: Searchable {
    var titleField: String { title }
    var subtitleField: String { subtitle }
    var additionalField: String { additional }
}

let initialList: [Item] = [Item(title: "First Title", subtitle: "First Subtitle", additional: "First Add"),
                           Item(title: "Second Title", subtitle: "Second Subtitle", additional: "Second Add pp"),
                           Item(title: "Third Title", subtitle: "Third Subtitle", additional: "Third Add"),
                           Item(title: "Fourth Title", subtitle: "Fourth Subtitle", additional: "Fourth Add")]

Search Parameters, with either functions (since you seem to want func), or closures (and not clouser as you write it, but which aren't really closures in your case)

var closureTitle: ((String, Searchable) -> Bool)

closureTitle = { searchText, aSearchable in
    return aSearchable.titleField.contains(searchText)
}
var closureSubtitle: ((String, Searchable) -> Bool)

closureSubtitle = { searchText, aSearchable in
    return aSearchable.subtitleField.contains(searchText)
}

func functionAdditional(_ searchText: String, _ aSearchable: Searchable) -> Bool {
    return aSearchable.additionalField.contains(searchText)
}

Your main search function:

func search<T: Searchable>(query: String, mainDataList:[T], filters: [((String, T) -> Bool)], completionHandler: @escaping ([T], Error?) -> Void)
{
    let queue = DispatchQueue(label: "SomeQueue", attributes: .concurrent)
    queue.asyncAfter(deadline: .now() + 0.3, execute: {

        //The logic:
        // We do a clasic `filter` call on mainDataList
        // For each item inside it, we check if at least one of the filters allows it
        // If yes, we keep it (return true), else, we discard it (return false)
        // That's a OR logic: If you find the search text query in ANY of three properties
       // If you want an AND (meaning, text has to be present in EACH property to be valid), change the `contains` with
    `filters.allSatisfy { aFilter in aFilter(query, aSearchable) }` which should do the trick
        let filteredList = mainDataList.filter { aSearchable in
            let keepElement = filters.contains { aFilter in
                aFilter(query, aSearchable) == true
            }
            return keepElement
        }

        DispatchQueue.main.async {
            completionHandler(filteredList, nil)
        }
    })
}

In use:

//With only closures
search(query: "o", mainDataList: initialList, filters: [closureTitle, closureSubtitle]) { filteredList, error in
    print("Only closures: \(filteredList)")
}

//With closures and functions
search(query: "pp", mainDataList: initialList, filters: [closureTitle, closureSubtitle, functionAdditional]) { filteredList, error in
    print("Closures & Functions: \(filteredList)")
}

Output:

$>Only closures: [<Item>: T: Second Title - S: Second Subtitle - A: Second Add pp, <Item>: T: Fourth Title - S: Fourth Subtitle - A: Fourth Add]
$>Closures & Functions: [<Item>: T: Second Title - S: Second Subtitle - A: Second Add pp]

Upvotes: 1

Related Questions