Обработчик завершения swift 3 возвращает переменную из функции

Я запутался в синтаксисе обработчика завершения в Swift 3.

В приведенной ниже функции после разбора файла xml из вызова веб-службы она должна вернуть переменную (array [String:String]).
Моя попытка ниже, но очевидно, что она неверна.

  enum HistoryKey {
  case success([String:String])
  case failure(String)
 }

 private func getHistoryKeys(searchterm: String, completion: @escaping () -> HistoryKey) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }
    }
    task.resume()

    if keys.isEmpty {
        return .failure("no historyKeyDictionary")
    }else{
        return .success(keys)
    }

}// End of func

Я хочу использовать эту функцию следующим образом

 let result = self.getHistoryKeys(searchTerm)

person tim    schedule 23.05.2017    source источник
comment
@EricAya он не пытается вернуть значение из функции, а скорее передает его другому обработчику завершения.   -  person Tristan Beaton    schedule 23.05.2017
comment
Твое право. Я этого не заметил. @ЭрикАя   -  person Tristan Beaton    schedule 23.05.2017
comment
Пожалуйста, примите ответ, чтобы другие могли найти его быстрее. Спасибо. Нажмите галку   -  person Vlad Pulichev    schedule 08.06.2017


Ответы (6)


Две проблемы:

  • Обработчик завершения передает экземпляр HistoryKey и не имеет возвращаемого значения, поэтому подпись должна быть наоборот.
  • Вызов обработчика завершения должен быть внутри блока завершения задачи данных.

Чтобы иметь возможность анализировать полученные данные за пределами блока завершения, верните data в случае успеха.

enum ConnectionResult {
   case success(Data)
   case failure(Error)
}

private func getHistoryKeys(searchterm: String, completion: @escaping (ConnectionResult) -> ()) {
   let url = PubmedAPI.createEsearchURL(searchString: searchterm)
   let task = session.dataTask(with: url) { (data, response, error) in
       if let error = error {
          completion(.failure(error))
       } else {
          completion(.success(data!))
       }
  }
  task.resume()
}

и назови это

getHistoryKeys(searchterm: String) { connectionResult in 
    switch connectionResult {
       case .success(let data): 
           let myParser = XMLParser(data: data)
           myParser.delegate = self
           myParser.parse()
           // get the parsed data from the delegate methods

       case .failure(let error): print(error)
    }
}
person vadian    schedule 23.05.2017
comment
Извините, я только что добавил правку, чтобы дать представление о том, как я хотел бы использовать эту функцию. Будет ли ваше решение работать для вышеуказанного? - person tim; 23.05.2017
comment
Нет. Либо вы должны вернуть полученный Data, потому что синтаксический анализатор работает асинхронно и анализирует XML вне функции, либо вы должны назначить обработчик завершения переменной, чтобы получить сильную ссылку и вызвать обработчик завершения из метода делегата XMLParser. Кстати: ваш массив в случае перечисления является словарем. - person vadian; 23.05.2017
comment
PS: я обновил ответ, чтобы передать полученные данные в обработчик завершения. Поместите код для обработки результата синтаксического анализатора XML в метод делегата синтаксического анализатора. - person vadian; 23.05.2017
comment
Я понимаю ваше решение - получение переменной из метода делегата, например, parserDidEndDocument. Но не могли бы вы показать мне, как вызвать обработчик завершения, например, из parserDidEndDocument? - person tim; 24.05.2017

Вы не используете блок completion.
Используйте его как:

private func getHistoryKeys(searchterm: String, completion: @escaping (_ keys: Array) -> Void) {
    //do the magic
    completion(keys)
}

Затем вы можете вызвать эту функцию как:

getHistoryKeys(searchterm: "str") { (keys) in 
    print("\(keys)")
}
person D4ttatraya    schedule 23.05.2017

Свифт 4.2

enum HistoryKey {
    case success([String:String])
    case failure(String)
}

func myFunction(str: String, completionHandler: @escaping (HistoryKey) -> ()){
     completion(.success([String:String]))
     //OR
     completion(.failure(""))
}

myFunction(str: String) { result in 
    switch result {
       case .success(let data): break;
       case .failure(let error): break;
    }
}

ИЛИ

func myFunction(str: String, completionHandler: @escaping (String) -> ()){
     completionHandler("")
} 

myFunction(str: "someThing", completionHandler: {(str) in

})
person ZAFAR007    schedule 22.11.2018

Верните результат в качестве аргумента в обработчике завершения:

private func getHistoryKeys(searchterm: String, completion: @escaping (result: HistoryKey) -> Void) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }

        //DispatchQueue.main.async { // if you want to do UI stuff dispatch calls to completion() on the main queue
        if keys.isEmpty {
            completion(.failure("no historyKeyDictionary"))
        } else{
            completion(.success(keys))
        }
        //}
    }
    task.resume()   
}

И назовите это так:

getHistoryKeys("searchMe") { (result: HistoryKey) in
    print(result)
}
person shallowThought    schedule 23.05.2017
comment
Извините, я только что добавил правку, чтобы дать представление о том, как я хотел бы использовать эту функцию. Будет ли ваше решение работать для вышеуказанного? потому что ваше возвращаемое значение недействительно - person tim; 23.05.2017
comment
self.getHistoryKeys(searchTerm) возвращается немедленно, но загрузка занимает время. Вы должны дождаться этого. - person shallowThought; 23.05.2017

 enum HistoryKey {
    case success([String: String])
    case failure(String)
 }

 private func getHistoryKeys(searchterm: String, completion: @escaping (_ result: HistoryKey) -> Void) {
    let url = PubmedAPI.createEsearchURL(searchString: searchterm)
    let request = URLRequest.init(url: url as URL)
    let task = session.dataTask(with: request) { (data, response, error) in

        if let theData = data{
            let myParser = XMLParser.init(data: theData)
            myParser.delegate = self
            myParser.parse()
        }

        if keys.isEmpty {
            completion(.failure("no historyKeyDictionary"))
        } else {
            completion(.success(keys))
        }
    }
    task.resume()    
} // End of func

Что-то такое. Измените объявление @escaping и выполните завершение вместо возврата.

Надеюсь, поможет.

person Vlad Pulichev    schedule 23.05.2017

Насколько я вижу, должно быть

private func getHistoryKeys(searchterm: String, completion: @escaping (HistoryKey) -> ())

Также вместо return следует использовать completion(.failure("no historyKeyDictionary")) или completion(.success(keys)).

person Tristan Beaton    schedule 23.05.2017