-1

Further to my last question, which thankfully I received help with, I'm trying to get this little bit of code to return a value for me so I can plug it into the text field on the storyboard. The code is working - but I can't work out how to return the value I need. myBalance, or btn_balance.

I've tried a "return value" but it's ignoring it. This is my modified code. This prints "Hello" in the text field, but not the value. I'm a bit of a noobie at this and am afraid that I've jumped into the deep end a bit. I can return data from a function as in normal sessions, but this 'task' has me beaten.

class GetBalanceViewController: UIViewController {

var myBalance :String = ""
var btn_balance :String = ""

@IBOutlet weak var displayBalance: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    //makeGetCall()

    //display the balance on the screen
   // print(makeGetCall(myBalance: btn_balance))
    myBalance =  (makeGetCall(myBalance: btn_balance))
    print("Hello...: \(myBalance)") // blank
    displayBalance.text = (makeGetCall(myBalance: btn_balance)) + "Hello" // displays "Hello" 


    // Do any additional setup after loading the view.

}

The function - with my modification, is this.

    func makeGetCall(myBalance: String) -> String  {
    // Set up the URL request
    let todoEndpoint: String = "https://api.jsecoin.com/v1.7/balance/auth/0/"
    //let todoEndpoint: String = "https://api.jsecoin.com/v1.7/ledger/auth/"
    //let todoEndpoint: String = "https://api.jsecoin.com/v1.7/balance/checkuserid/73276/auth/"
    let apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"


    guard let url = URL(string: todoEndpoint) else {
        print("Error: cannot create URL")
        return "Error"
    }
    var urlRequest = URLRequest(url: url)

    urlRequest.setValue(apiKey, forHTTPHeaderField: "Authorization")
    urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
    // set up the session
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    // make the request
    let task = session.dataTask(with: urlRequest) {
        (data, response, error) in
        // check for any errors
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error!)
            return
        }


        // make sure we got data
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }
        print("Got data")
        // parse the result as JSON, since that's what the API provides
        do {
            guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
                as? [String: Any] else {
                    print("error trying to convert data to JSON")
                    return
            }
            // now we have the todo
            // let's just print it to prove we can access it
            print("The todo is: " + todo.description)

            // the todo object is a dictionary
            // so we just access the title using the "title" key
            // so check for a title and print it if we have one
            let index = todo.index(forKey: "notification")

            let btn_balance = (todo[index!].value)

            let myBalance = btn_balance
            print("myBalance I: \(myBalance)")
            print("btn_balance I: \(btn_balance)")

          /*
            for (key,value) in todo
            {
                print("\(key) : \(value)")

            }
           */
        /*
            guard let todoTitle = todo["balance"] as? String
                else {
              //  print("Could not get todo title from JSON")
                return
                }
         */
           //print("The title is: " + todoTitle)
            } catch  {
            print("error trying to convert data to JSON")
            return
        }

    }
   task.resume()
     return btn_balance
}

The Original is simply this.

makeGetCall()

func makeGetCall() {
.....
   }
   task.resume()

}

The program displays the data on in the console ok

The todo is: ["notification": Your balance is 5646.65 JSE, "balance": 5646.65, "success": 1]
myBalance I: Your balance is 5646.65 JSE
btn_balance I: Your balance is 5646.65 JSE

So that's the question, how can I get that value, which I can get as you see, back to the storyboard.

Harry McGovern
  • 517
  • 5
  • 19

1 Answers1

1

Since dataTask works asynchronously, you cannot simply return the value from makeGetCall. Instead, you'll have to update the data model or UI inside the closure.

You could make makeGetCall return Void, and add a DispatchQueue.main.async call inside the completion handler, to update the UI (and maybe something more, like updating the properties).

Something like:

func makeGetCall(myBalance: String) -> ()  {
    // ...

    let task = session.dataTask(with: urlRequest) {
        (data, response, error) in

        // ..

        do {
            // ...

            DispatchQueue.main.async {
                if let balanceString = todo[index!].value as? String {    
                    self.btn_balance = balanceString
                    self.displayBalance.text = balanceString
                } else {
                    // Ooops
                    self.displayBalance.text = "?? unknown ??"
                }
            }
            // ...
        }

    }
   task.resume()
}

Btw: why do you use local variables btn_balance and myBalance? I guess you mean selft.btn_balance and self.myBalance. If so, you should also only write those values inside the DispatchQueue.main.async closure.

Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34
  • Sorry, I don't quite understand. I've gone back to the original call super.viewDidLoad() makeGetCall() and I'm getting the error. Module 'Dispatch' has no member named 'main' – Harry McGovern Jul 19 '18 at 13:12
  • Where do you need help? In `viewDidLoad`, you call `makeGetCall`. Since there is no return value, you do not set any `displayBalance.text` here. This is done inside `session.dataTask` in `Dispatch.main.async`. – Andreas Oetjen Jul 19 '18 at 13:16
  • Yes, I now know that, but I'm getting Module 'Dispatch' has no member named 'main' as a result of your code that I put into the body of func makeGetCall() – Harry McGovern Jul 19 '18 at 13:22
  • Arrg, my fault. It's `DispatchQueue`... – Andreas Oetjen Jul 19 '18 at 13:24
  • Ok, thanks. I had an idea it must have been that from searching stackoverflow. However... now I get this DispatchQueue.main.async { self.btn_balance = (todo[index!].value) self.displayBalance.text = btn_balance } Error: Cannot assign value of type 'Any' to type 'String' against both btn_balance and displayBalance. sigh... – Harry McGovern Jul 19 '18 at 13:30
  • That's because todo maps to `Any`. You can safely do it with `if let`, I'll change my answer accordingly... – Andreas Oetjen Jul 19 '18 at 13:34
  • Excellent- Thanks Andreas. I'll now print this out and study it carefully. I appreciate your help. – Harry McGovern Jul 19 '18 at 13:40