Completion handler swift 3 return a variable from function
20,165
Solution 1
Two issues:
- The completion handler passes a
HistoryKey
instance and has no return value so the signature must be the other way round. - The call of the completion handler must be inside the completion block of the data task.
To be able to parse the received data outside the completion block return the data
on success
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()
}
and call it
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)
}
}
Solution 2
You are not using completion
block.
Use it like:
private func getHistoryKeys(searchterm: String, completion: @escaping (_ keys: Array) -> Void) {
//do the magic
completion(keys)
}
Then you can call this function as:
getHistoryKeys(searchterm: "str") { (keys) in
print("\(keys)")
}
Solution 3
Swift 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;
}
}
OR
func myFunction(str: String, completionHandler: @escaping (String) -> ()){
completionHandler("")
}
myFunction(str: "someThing", completionHandler: {(str) in
})
Solution 4
Return the result as an argument in the completion handler:
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()
}
And call it like this:
getHistoryKeys("searchMe") { (result: HistoryKey) in
print(result)
}
Author by
tim
Updated on November 22, 2020Comments
-
tim over 3 years
I am confused surrounding the syntax for a completion handler in swift 3.
In the function below, after parsing an
xml
file from a web service call, it should return a variable (anarray [String:String]
).
My attempt is below, but obviously it is incorrect.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
I want to use this function as follows
let result = self.getHistoryKeys(searchTerm)
-
tim almost 7 yearsSorry, i have just added an edit to give an idea on how i would like to use the function. Would your solution work for above? because your return value is void
-
tim almost 7 yearsSorry, i have just added an edit to give an idea on how i would like to use the function. Would your solution work for above?
-
shallowThought almost 7 yearsNo.
self.getHistoryKeys(searchTerm)
returns immediately, but your download takes time. You have to wait for it. -
vadian almost 7 yearsNo it does not. Either you have to return the received
Data
because the parser works asynchronously and parse the XML outside the function or you have to assign the completion handler to a variable to get a strong reference and call the completion handler from the XMLParser delegate method. Btw: your array in the enum case is a dictionary. -
vadian almost 7 yearsPS: I updated the answer to pass the received data in the completion handler. Put the code to handle the result of the XML parser into the parser delegate method.
-
tim almost 7 yearsI understand your solution - obtaining the variable from the delegate method e.g parserDidEndDocument. But, could you show me how to call the completion handler from parserDidEndDocument for instance?