google / promises

Promises is a modern framework that provides a synchronization construct for Swift and Objective-C.
Apache License 2.0
3.8k stars 295 forks source link

Sometimes Post Request Timeout #179

Closed brandon2727 closed 2 years ago

brandon2727 commented 2 years ago

I've integrated Google Promises in our iOS project and I "sometimes" see Post requests timing out around 1 in 5 or so. Even though on my Proxy (using Charles) they show as completed (usually in under a second). However the flow of control doesn't ever reach the completion block of URLSession.shared.dataTask(with: request). Not sure exactly what im doing wrong here. Not putting the successful flow since it doesnt seem relevant.

Here is my code

return Promise<Data>(on: .global(qos: .background), { (fullfill, reject) in
            guard NetworkReachability.status != .unavailable else {
                reject(Error.noConnection)
                return
            }

            let session = URLSession.shared
            var request = URLRequest(url: api.fullURL, timeoutInterval: 30.0)
            request.httpMethod = api.method.rawValue
            request.allHTTPHeaderFields = api.headers

            if case .requestParameters(bodyParameters: let bodyParameters) = api.task {

                guard let jsonData = try? JSONSerialization.data(withJSONObject: bodyParameters, options: .prettyPrinted) else {
                    throw Error.encodingFailed
                }
                request.httpBody = jsonData
            }

            session.dataTask(with: request) { (data, response, error) in

                if let error = error {
                    print("Found error: \(error.localizedDescription) calling API: \(api.fullURL)")
                    Log.error("Found error: \(error.localizedDescription) calling API: \(api.fullURL)")
                    reject(Error.apiCallError(error.localizedDescription))
                    return
                }
chuckhays commented 2 years ago

Are you missing the code in your post where you call fullfill?

Oops, I didn't read the text closely enough, you said you omitted it for clarity. Might be useful to see the whole thing though anyway.

chuckhays commented 2 years ago

If the completion block from URLSession.shared.dataTask(with: request) never gets executed, the problem is likely not with the Promises library. You could test it in theory by doing the same requests without using Promises and seeing if the same behavior remains.

brandon2727 commented 2 years ago

Good point I will look there and try without promises. Odd thing is that it can be the same request but on that 5th time the completion block doesn't get called and it times out

brandon2727 commented 2 years ago

Okay so I did some digging and the below code without promises does not cause the time out issue. Yea I know completion handlers are old school ha. Heres the code that has no timeout issue.

public static func newGenericRequest(with api: EndPoint, success : @escaping (_ data: Any?) -> Void, failure : @escaping (_ error: NSError?) -> Void) {
        let request = try? buildRequest(with: api)

        print("create datatask")
        URLSession.shared.dataTask(with: request!) { data, response, error in
            print("response on background thread")

            DispatchQueue.main.async {
                print("inside response on main thread")

                if let error = error {
                    print("Found error: \(error.localizedDescription) calling API: \(api.fullURL)")
                    Log.error("Found error: \(error.localizedDescription) calling API: \(api.fullURL)")
                    failure(NSError())
                    return
                }

                if let response = response as? HTTPURLResponse {

                    guard let data = data else {
                        return
                    }

                    let result = self.handleNetworkResponse(data, response, api)

                    switch result {
                    case .success:
                        success(data)

                    case .failure(let _):
                        failure(NSError())
                    }
                }
            }

        }.resume()
    }

Ive tried this like 35 times. I do notice that sometimes there is a small hiccup (meaning I see the call complete on my proxy but iOS takes a few more secs before the response call back is called). URLSession.shared.dataTask always returns within the 30 sec timeout limit I set. Im thinking it might have something to do with threading perhaps? If we make a network call with promises shouldn't we have the promise be on a background thread? Maybe I should have the response side of the url session data task run on main thread? I didn't want to do that because I thought thats what promises try to solve. Could be completely wrong here. I do have the latest google promises pod 2.0.0

brandon2727 commented 2 years ago

yea error was on my side. no issues here. sorry i didnt post this quicker

chuckhays commented 2 years ago

Thanks for the update.