Taichi Kato Taichi Kato - 12 days ago 5
iOS Question

Swift Function Returning Value Before It has finished processing

Basically I have a code for posting simple data to a server and will return a boolean value

success
if the post request was successful but it seems to be that the boolean value is returned before even the data is processed, am I doing something wrong?

public func postRequest(rawText: String) -> Bool {
var success = true
let destUrl = "http://api.getquesto.com:8080/upload/"
var request = URLRequest(url: URL(string: destUrl)!)
request.httpMethod = "POST"
let postString = rawText
request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
// request.setValue("compute", forHTTPHeaderField: "Questo-Query")
// request.setValue("Fuck you", forHTTPHeaderField: "quizTitle")

request.httpBody = postString.data(using: .utf8)
print(request.httpBody!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
success = false
print(success)

return
}

if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}

let responseString = String(data: data, encoding: .utf8)
do {
if let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: UInt(0))) as? [String: Any] {
print("json \(json)")
} else {
print("can not cast data")
success = false

}
} catch let error {
print("cant parse json \(error)")
success = false
print(success)

}

print("responseString = \(responseString)")
let dataString = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
//print(dataString)
//print("responseString = \(responseString)")
constantVariables.rawQuestionData = dataString as! String
let processedResults = dataString?.replacingOccurrences(of: "\\n", with: " ")
print("processed results = " + (processedResults! as String))
let newArray = processedResults!.components(separatedBy: ", \"")
//print(newArray)

for index in 0...(newArray.count - 1) {
if index%2 == 0 {
constantVariables.questions.insert(newArray[index].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), at: index/2)

// ConstantsArray.questionArray.append(newArray[index])
// print("question array: " + ConstantsArray.answerArray[index/2])
}else{
constantVariables.answers.insert(newArray[index].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), at: (index-1)/2)

// ConstantsArray.questionArray.append(newArray[index])
print("answer array: " + constantVariables.answers[(index-1)/2])

}
}
}
task.resume()
print(success)
return success
}

Answer

This is happening because the function directly returns the value of success, dataTask works asynchronously, so, the function should NOT wait until dataTask finishes the parsing to edit the value of success, i.e: return success is executed before dataTask edits the value of success.

I suggest to let the function handles a completion closure instead of direct returning of Bool.

Your function should be similar to:

public func postRequest(rawText: String, completion: @escaping (_ success: Bool) -> ()) {
    var success = true
    let destUrl = "http://api.getquesto.com:8080/upload/"
    var request = URLRequest(url: URL(string: destUrl)!)
    request.httpMethod = "POST"
    let postString = rawText
    request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
    //    request.setValue("compute", forHTTPHeaderField: "Questo-Query")
    //    request.setValue("Fuck you", forHTTPHeaderField: "quizTitle")

    request.httpBody = postString.data(using: .utf8)
    print(request.httpBody!)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {                                                 // check for fundamental networking error
            print("error=\(error)")
            success = false
            print(success)

            return
        }

        if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {           // check for http errors
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(response)")
        }

        let responseString = String(data: data, encoding: .utf8)
        do {
            if let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: UInt(0))) as? [String: Any] {
                print("json \(json)")
            } else {
                print("can not cast data")
                success = false

            }
        } catch let error {
            print("cant parse json \(error)")
            success = false
            print(success)

        }

        print("responseString = \(responseString)")
        let dataString = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
        //print(dataString)
        //print("responseString = \(responseString)")
        constantVariables.rawQuestionData = dataString as! String
        let processedResults = dataString?.replacingOccurrences(of: "\\n", with: " ")
        print("processed results = " + (processedResults! as String))
        let newArray = processedResults!.components(separatedBy: ", \"")
        //print(newArray)

        for index in 0...(newArray.count - 1) {
            if index%2 == 0 {
                constantVariables.questions.insert(newArray[index].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), at: index/2)

                //        ConstantsArray.questionArray.append(newArray[index])
                //        print("question array: " + ConstantsArray.answerArray[index/2])
            }else{
                constantVariables.answers.insert(newArray[index].replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: "]", with: "").replacingOccurrences(of: "[", with: ""), at: (index-1)/2)

                //        ConstantsArray.questionArray.append(newArray[index])
                print("answer array: " + constantVariables.answers[(index-1)/2])

            }
        }

        completion(success)
    }
    task.resume()
    print(success)
}

In Swift 3, you should use @escaping, for more information, you might want to check this answer.

Calling:

postRequest(rawText: "rawText", completion: { success in
    print(success)
})

Now, it should wait untill dataTask finish it's parsing, and then, the code in the completion will be called.

Hope it helped.