1

I'm not sure why my app crashes due to a "request timed out" error. It absolutely doesn't make sense to me because I'm getting a response back with the data i'm expecting.

Some context:

Every time the user scrolls the mapview, I make a call to the server to retrieve some info about a location, which comes back fine, I believe. Do I need to close the connection after I'm done? Is it because i'm scrolling too much and in turn i'm making so many requests that it overwhelms the server? I absolutely have no clue as to why this might be happening, but I do know that the data I'm looking to get back is coming back successfully, and then out of no where it'll say "request timed out" and I got a nil for the response. Below is my code. Thank you!

Additionally, how would I handle a timed out request so my app does not crash?

       let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in

        let httpResponse = response as? NSHTTPURLResponse

        if httpResponse?.statusCode != 200 {
            print(httpResponse?.statusCode)
        }

        if error != nil {
            print("Localized description error: \(error!.localizedDescription)")
        }

        do {
            //Store JSON data into dictionary
            self.locationsObjectDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSMutableDictionary

            //print(self.locationsObjectDictionary)

        } catch {
           print("JSON object could not be retrieved: \(error)")
        }
        completion()
    }

    // Start the session
    task.resume()
}

    let qos = DISPATCH_QUEUE_PRIORITY_HIGH
    let queue = dispatch_get_global_queue(qos, 0)

    dispatch_async(queue) {
        self.fetchLocations() {
            self.addLocationInfoToPins()

            dispatch_async(dispatch_get_main_queue(), {

                self.mapView.addAnnotations(self.locationPins)

            })
        }
    }
Mihado
  • 1,487
  • 3
  • 17
  • 30
  • Some clarifications, on what callback of the delegate of the mapview you are making request? `resume()` has to be called after you have assigned `completionBlock` to the task. – Fahri Azimov Mar 08 '16 at 04:33
  • What do you exactly mean? I'm a tad confused. I'll post some additional code in hopes of some clarification so you can see where I call my server. – Mihado Mar 08 '16 at 04:40
  • As you can see, I make the call on a background thread and then update the annotations on the main thread. – Mihado Mar 08 '16 at 04:42
  • I was under the impression that this was happening because I wasn't calling `res.end()` on the server, however that's not the case as I just discovered that it's still crashing – Mihado Mar 08 '16 at 04:52
  • It should be because you are making too many requests. Consider making a request when user stops scrolling the map, or limit the number of the requests. Also, you didn't post any crash logs, please add crash logs to the question. – Fahri Azimov Mar 08 '16 at 04:59
  • The only thing I get in the console is `Localized description error: The request timed out. fatal error: unexpectedly found nil while unwrapping an Optional value (lldb) ` – Mihado Mar 08 '16 at 05:17
  • My `self.locationsObjectDictionary` is an optional, so I don't get why the app crashes if the server returns nil – Mihado Mar 08 '16 at 05:18
  • Mikado; did you try watching the performance of your app as you test it? I have a vague recollection you you need to return session objects – user3069232 Mar 08 '16 at 05:26
  • I'll do that in the morning. I have a hunch as to what's going on – Mihado Mar 08 '16 at 05:26

2 Answers2

2

You are unwrapping data even though error is not nil.

NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers)

This is what I would recommend:

public typealias InternalCompletionHandler = (completionStatus: Bool, error: NSError? ) -> Void


func  post(urlStr: String, _ params: [String: String], _ action: JupiterAction, completionHandler: ) {
    let url = NSURL(string: urlStr)

    let session = NSURLSession(configuration: self.sessionConfig)

    let request = NSMutableURLRequest(URL: url!)

    let task = session.dataTaskWithRequest(request) { 
       (data, response, error) -> Void in

        let httpResponse = response as? NSHTTPURLResponse

        if httpResponse?.statusCode != 200 {
            print(httpResponse?.statusCode)
        }
        var itWorked = true
        if ( error != nil ) {
            print("Localized description error: \(error!.localizedDescription)")
            itWorked = false
        } else {
            do {
                //Store JSON data into dictionary
                self.locationsObjectDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSMutableDictionary
                //print(self.locationsObjectDictionary)

            } catch {
               print("JSON object could not be retrieved: \(error)")
               itWorked = false
            }
        }
        // Pass the error back up the line so you can present the user with some sensible feedback if you want.
        // Move this out here so pass or fail, the callback happens.
        completion(itWorked, error)
    }
}

If there was an error, data is probably nil and cannot be unwrapped. In my own apps, I do the same sort of thing. What I do is to check to see if error is nil and if it is, then I do nothing else. If error is not nil, then it is safe to process data.

ryantxr
  • 4,119
  • 1
  • 11
  • 25
0

Okay so I found out what the problem was! This isn't a client side issue. On the server I wasn't allowing for a response to occur if there were no results to be returned. So when the user would scroll out over an area of the mapview where there was no data to be retrieved, the server wouldn't send back a response due to how I structured my code.

Excuse me for my ignorance, I'm still new to all this stuff! Many thanks!

Mihado
  • 1,487
  • 3
  • 17
  • 30