0

I am trying to get the returned value from a PHP script in Swift. However, it seems as though I keep getting the error:

Unexpectedly found nil while unwrapping an Optional value

Here is the class:

var value: String!

func run(idNumber: Int) {
    let request = NSMutableURLRequest(url: URL(string: "https://mywebsite.com/file.php")!)
    request.httpMethod = "POST"
    let postString = "a=Hello"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in

        if error != nil {
            //answer = error;
        }

        let answerString = String(data: data!, encoding: String.Encoding.utf8)
        self.value = answerString
    }
    task.resume()
}

func getValue() -> String{
    return value
}

The error occurs when calling the getValue() function. However, when I print out the "answerString" as soon as it is created, it prints out successfully!

The functions are called here:

let access = ApiAccess()
access.run(idNumber: 0)
print(access.getValue())
Enrique Bermúdez
  • 1,740
  • 2
  • 11
  • 25
Blockhead7360
  • 145
  • 10

3 Answers3

1

Making a request is an asynchronous task. You need to wait the closure callback to be call before calling getValue.

You can add a closure callback to your run method. That way you will know when the request has finished and you can print the result:

var value: String!

func run(idNumber: Int, @escaping callback: () -> Void) {

    let request = NSMutableURLRequest(url: URL(string: "https://mywebsite.com/file.php")!)
    request.httpMethod = "POST"
    let postString = "a=Hello"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in

        if error != nil {
            //answer = error;
        }


        let answerString = String(data: data!, encoding: String.Encoding.utf8)
        self.value = answerString
        callback()

    }
    task.resume()

}

func getValue() -> String{
    return value
}

let access = ApiAccess()
access.run(idNumber: 0) {
   print(access.getValue())
}
Enrique Bermúdez
  • 1,740
  • 2
  • 11
  • 25
1

The issue is that the callback for URLSession.shared.dataTask() happens asynchronously, so you'll end up executing access.getValue() before your var value is ever set. This means that value is forcefully unwrapped before it has a value, which causes this error.

To workaround this, consider using promises, RxSwift, or similar async tools so that you only access values when available.

John Ellmore
  • 2,209
  • 1
  • 10
  • 22
1

Refactor your run(idNumber:) function to take a completion handler:

func run(idNumber: Int, completion: (String?, Error?)-> Void ) {
    let request = NSMutableURLRequest(url: URL(string: "https://mywebsite.com/file.php")!)
    request.httpMethod = "POST"
    let postString = "a=Hello"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in

        if error != nil {
           completion(nil, error)
        }
        let answerString = String(data: data!, encoding: String.Encoding.utf8)
        self.value = answerString
        completion(answerString, nil)
    }
    task.resume()
}

And call it like this:

let access = ApiAccess()
access.run(idNumber: 0) { result, error in
    guard let result = result else {
      print("No result. Error = \(error)")
      return
    }
    print("result = \(result)")
}

(Or use Futures or Promises, as mentioned by @JohnEllmore in his answer)

Duncan C
  • 128,072
  • 22
  • 173
  • 272