I have two controller.

  1. Maincontroller
  2. Dropdown

Maincontrollerallows users to open dropdown. Dropdown allows users to select a row from the list. selected row value will be passed to Maincontroller. In Dropdown controller there is searchBar. When user search something, it asks Maincontroller to get data for search query. Maincontroller will fetch data from Web API and return result to Dropdown.

I'm facing the issue when returning data from Maincontroller to Dropdown using closure.

i'm presenting Dropdown modally from Maincontroller as below.

            let searchVC = LiveSearchDropDown<DropDownTitleTCell>.init(configureCell: { (cell ,object) -> DropDownTitleTCell in

                cell.lblTitle.text = "Dummy"
                return cell


        }, selection: { (selectedObject) in

            print(selectedObject)
            self.dismiss(animated: false, completion: nil)
        }, search: { (query, spaging) -> LiveSearchResponse? in

            let res  = self.fetchPatients(query: query, forPaging: spaging, response: { (response) in

            })
            return res

        })
        self.present(searchVC, animated: true)

Following is the function where i'm fetching search data from Web API in Maincontroller. It returns Object of Type LiveSearchResponse.

    func fetchPatients(query searchText: String, forPaging : Paging, response: @escaping(LiveSearchResponse) -> ()) {

    let params = Prefs.getAPICallParameters()
    var responseData = LiveSearchResponse()

    APIManager.shared.jsonRequest(url: AppConstant.Patient.getPatientList, parameters: params, method: .post, encoding: JSONEncoding.default, onSuccess: { (resposeJSON) in
        if let patientList = resposeJSON["data"].array {


            if patientList.count > 0 {
                var data = [Patient]()

                //success retreived
                for patient in patientList {
                    data.append(Patient(json: patient))
                }

                if patientList.count < 20 {
                    forPaging.shouldLoadMore = false
                } else {
                    forPaging.shouldLoadMore = true
                }
                responseData.data = data
                responseData.error = nil

            } else {
                forPaging.status = .failed
            }
            response(responseData)

        } else {
            forPaging.status = .failed
            self.presentAlertWithTitle(title: "Error", message: "AppConstant.Patient.getPatientList data Key not found", options: "Ok", completion: { (option) in
            })
            response(responseData)
        }
    }) { (error) in
        forPaging.status = .failed
        self.presentAlertWithTitle(title: "Error", message: error.message, options: "Ok", completion: { (option) in
        })
        response(responseData)
    }
}

I'm getting compile time error on below block when i'm returning object from closure.

Cannot convert return expression of type '()' to return type 'LiveSearchResponse?'

search: { (query, spaging) -> LiveSearchResponse? in

            let res  = self.fetchPatients(query: query, forPaging: spaging, response: { (response) in

            })
            return res

I don't know how to return value to Closures after fetching data from async function.

EDIT 2

In Dropdown i've declared like

public let searchQuery: LiveSearchQuery
typealias LiveSearchQuery = (String, Paging) -> LiveSearchResponse?

Initializing

required init(configureCell: @escaping CellConfiguration, selection: @escaping Selection, search: @escaping LiveSearchQuery) {
    self.configureCell = configureCell
    self.selection = selection
    self.searchQuery = search
    super.init(nibName: nil, bundle: nil)
    self.modalPresentationStyle = .formSheet
    self.modalTransitionStyle = .crossDissolve
    self.preferredContentSize = CGSize(width: 400, height: 400)
}

and Calling it like

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    if let query = searchBar.text, !query.isEmpty {
        self.paging.status = .loading
        self.tableView.reloadData()
        let response = searchQuery(query, self.paging)
        if response?.error == nil {
            self.dataModels = response?.data as? [AnyObject]
        } else {
        }
        self.tableView.reloadData()
        searchBar.resignFirstResponder()
    } else {

    }
}

Can you suggest me the proper way to archive the goal?

1 Answers

2
Jack Hopkins On Best Solutions

If you're calling an async method then search shouldn't return, you should pass a completion block, it will look something like this I think:

search: {[weak self] (query, spaging, completion) in

    self?.fetchPatients(query: query, forPaging: spaging, response: { (response) in
        completion(response)
    })

Edit 1

I've got a bit more of an idea with the added info, you should be able to do something like this:

public let searchQuery: LiveSearchQuery
typealias LiveSearchQuery = (String, Paging, @escaping (LiveSearchResponse?)->())->()

required init(configureCell: @escaping CellConfiguration, selection: @escaping Selection, search: @escaping LiveSearchQuery) {
    self.configureCell = configureCell
    self.selection = selection
    self.searchQuery = search
    super.init(nibName: nil, bundle: nil)
    self.modalPresentationStyle = .formSheet
    self.modalTransitionStyle = .crossDissolve
    self.preferredContentSize = CGSize(width: 400, height: 400)
}

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    if let query = searchBar.text, !query.isEmpty {
        self.paging.status = .loading
        self.tableView.reloadData()
        searchQuery(query, self.paging) { [weak self] response in
            DispatchQueue.main.async {
                if response?.error == nil {
                    self?.dataModels = response?.data as? [AnyObject]
                } else {
                }
                self?.tableView.reloadData()
                searchBar.resignFirstResponder()
            }
        }
    } else {

    }
}