Reputation: 894
Recently, I attempted to write my own Telegram Bot API. However, the project has seem to have hit a brick wall with URLSession (formerly NSURLSession) issues.
The call structure is as follows:
getMe() -> getData() -> NSURLSession
Ideally, I would like to have the data returned from NSURLSession
passed back to getMe()
for the application to process. However, this has not proven possible with the methods I have tried.
Below is the code I have been using. synthesiseURL()
generates the URL that the app should open the session to in order to perform the action on the Telegram Bot API. A template of the URL generated by synthesiseURL()
is https://api.telegram.org/bot\(token)/\(tgMethod)
.
// NSURLSession getData: gets data from Telegram Bot API
func getData(tgMethod: String, arguments: [String] = [String](), caller: String = #function) {
let url = synthesiseURL(tgMethod: "getMe"), request = NSMutableURLRequest(url: url)
var receivedData = String()
let session = URLSession.shared.dataTask(with: request as URLRequest) { data, response, err in
if err != nil {print(err!.localizedDescription); return}
DispatchQueue.main.async {
receivedData = String(data: data!, encoding: String.Encoding.nonLossyASCII)!
print(receivedData)
}
}
session.resume()
}
I have been trying to get getData
to pass receivedData
, which contains the Bot API's response, back to the function getMe
.
func getMe() -> String {
HTTPInterface(botToken: token).get(tgMethod: "getMe")
return [???] // here's where the data from getData() should come
}
I have tried completion handlers, callbacks, asynchronous calls to the main thread etc, but none seem to be working as expected (getMe()
returns an empty string).
Why is this so, and can it be fixed?
Upvotes: 1
Views: 453
Reputation: 13679
The fundamental issue is that your getMe()
function is declared as having an immediate String
return type, but it depends on a delayed / asynchronous call to get that string. The timeline looks something like this:
getMe()
is called by some client codegetMe()
kicks of the method that launches a URLSession
to get the datagetMe()
moves to the next line of execution and returns a string (still empty at this point). The getMe()
function has now returned and the client code execution has continued forward with the empty String
resultURLSession
completes with data, but execution has already moved on so the data doesn't get used anywhereThe easiest fix is to make your getMe
function not have a return type, but to also call back to a closure parameter when the URLSession data comes back, something like:
func getMe(callback:String->()) {
//getData and pass a closure that executes the callback closure with the String data that comes back
}
The less easy fix is to use a technique like dispatch semaphores to prevent getMe()
from returning a result until the URLSession data comes back. But this sort of approach is likely to stall your main thread and is unlikely to be the right choice.
Upvotes: 1