Swift OOP: как инкапсулировать поведение поиска - PullRequest
0 голосов
/ 11 июня 2019

В настоящее время у меня есть следующий код:

import UIKit

struct ToBeSearched {
    var value1 = "1"
    var value2 = "3"
    var value3 = "3"
    var boolean = true
}

var data = [ToBeSearched]()
var completeData = [ToBeSearched]()

public func updateSearchResults(for searchController: UISearchController) {
    if let text = searchController.searchBar.text,
        !text.isEmpty {
        data = completeData.filter{
            // How to encapsulate this behavior, i.e. to extend it to use new values (value2, value2...)
            $0.value1.lowercased().contains(text.lowercased())
        }
    } else {
        data = completeData
    }
    reloadResults()
}

Это простой код поиска, который находит все значения, где value1 содержит текст для поиска.

Что если бы я хотелсоответствуют также value2 и value3?Как я могу извлечь логику поиска, чтобы ее можно было изменить отдельно, не касаясь основного кода.

В настоящее время мне придется использовать оператор binary OR для прохождения всех случаев:

let searchText = text.lowercased()
$0.value1.lowercased().contains(searchText) ||
$0.value2.lowercased().contains(searchText) ||
$0.value3.lowercased().contains(searchText)
...

Есть ли более элегантный способ достижения того же результата?

Ответы [ 2 ]

1 голос
/ 11 июня 2019

Метод 1: Укажите свойства для поиска, используя [KeyPath]:

Если вы просто хотите гибко указать, какие поля искать в структуре ToBeSearched, вы можете передать массив [KeyPath] свойств для поиска и использовать contains с закрытием внутри filter для проверьте, содержит ли какое-либо из свойств, указанных в keyPaths, текст, который вы ищете:

public func updateSearchResults(for searchController: UISearchController, using keyPaths: [KeyPath<ToBeSearched, String>]) {
    if let text = searchController.searchBar.text,
        !text.isEmpty {
        data = completeData.filter { element in
            keyPaths.contains { keyPath in element[keyPath: keyPath].lowercased().contains(text.lowercased()) }
        }
    } else {
        data = completeData
    }
    reloadResults()
}

Пример:

Для поиска value1 и value2:

updateSearchResults(for: searchController, using: [\.value1, \.value2])

Метод 2: Передать закрытие для метода filter:

public func updateSearchResults(for searchController: UISearchController, using filterProc: (ToBeSearched) -> Bool)  {
    if let text = searchController.searchBar.text,
        !text.isEmpty {
        data = completeData.filter(filterProc)
        }
    } else {
        data = completeData
    }
    reloadResults()
}

Пример:

let filterProc: (ToBeSearched) -> Bool = {
    $0.value1.lowercased().contains(searchText) ||
    $0.value2.lowercased().contains(searchText)
}

updateSearchResults(for: searchController, using: filterProc)
0 голосов
/ 11 июня 2019

Я надеюсь, что это должно решить, что вы ищете.Если структура фиксирована, вы можете реализовать следующий код, который инкапсулирует код сравнения внутри структуры.

struct ToBeSearched {
    var value1 = "1"
    var value2 = "3"
    var value3 = "3"
    var boolean = true

    func compareText(_ searchText: String) -> Bool {
        return value1.lowercased().contains(searchText.lowercased()) || value2.lowercased().contains(searchText.lowercased()) || value3.lowercased().contains(searchText.lowercased())
    }
}

Это позволит сравнить все значения без пропусков.

И обновить следующую строку кода.

$0.compareText(text)

Основная цель состоит в том, чтобы вы могли убедиться, что метод основной цели добавлен в структуру, поэтому его можно использовать в любом месте вместо сравнения каждого отдельного значения внутри filter метода.

Я надеюсь,это поможет вам.

...