Search code examples
swift3dismissurlrequest

timing issue after completion of URLRequest


i have a behavior i cant resolve. I have a controller (controller 1) where i check some defaults value. if not present (first time use of app: check login and pwd) i present (modal) a settings vc:

IN CONTROLLER 1
override func viewDidAppear(_ animated: Bool) {
    if(!isKeyPresentInUserDefaults(key: "username")) {
        NSLog("username not present")
        let vc = self.storyboard?.instantiateViewController(withIdentifier: "settings") as! SettingsViewController
        self.present(vc, animated: true, completion: nil)
    }
}

in this vc (controller 2) (also in the main storyboard) i have a button done. When pressed, it is associated to the:

IN CONTROLLER 2: SettingsVc -> ID : settings
@IBAction func doneSettings(sender: AnyObject) {
    if(isInternetAvailable()) {
        NSLog("internet available")

         login { (result) in
         switch result
         {
         case .Success(let result):
            //print(result)
            self.dismissSelf()
         break

         case .Failure(let error):
            print(error)
         break
         }
         }
     }
    else {
        NSLog("internet not available")
    }
}

the dismissSelf func is defined in the Settingsvc as:

func dismissSelf() {
    NSLog("dismissSettingsVC")
    self.dismiss(animated: false, completion: nil)
}

the login func is defined in another class, dealing with networking stuff and is as is:

func login(completion: @escaping (AsyncResult<[CustomUserObject]>)->())
{
    let myUrl = URL(string: "http://www.xxxx.com/api/api.php");
    var request = URLRequest(url:myUrl!)
    request.httpMethod = "POST"// Compose a query string
    let postString = "u=login&s=password&cmd=login";
    request.httpBody = postString.data(using: String.Encoding.utf8);

    let session = URLSession.shared
    let task = session.dataTask(with: request as URLRequest){
        (data, response, error) -> Void in
        if let error = error
        {
            completion(AsyncResult.Failure(error as NSError?))
        } else {
            let result: [CustomUserObject] = []//deserialization json data into array of [CustomUserObject]
            let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
            print("*********response data = \(responseString)")
            completion(AsyncResult.Success(result))
        }
    }
    task.resume()
}

So, I launch the app for the first time (no login, no pwd in defaults), then the Settingvc is presented. I press the done button with param hardcoded. The login is correctly called, I receive the answer correctly, and on completion i should dismiss the Settingvc. thing is, i see the NSLOG for dismiss, but the dismiss appears seconds after the network func completion (from 10 secs to up to a minute), which I can't understand. Why such a delay? any idea?

2017-11-27 21:54:33.262892 app[998:211517] username not present
2017-11-27 21:54:36.119754 app[998:211517] internet available
*********response data = 
Optional({"cmd":"login","success":"true","message":"login succeded"})
2017-11-27 21:54:38.472306 app[998:211542] dismissSettingsVC
2017-11-27 21:54:48.048095 app[998:211517] username not present

in this case, it took 10 sec to dismiss the Settingsvc after receiving the login results.

another one:

2017-11-27 22:04:20.364097 app[998:211517] internet available
*********response data = 
Optional({"cmd":"login","success":"true","message":"login succeded"})
2017-11-27 22:04:22.495642 app[998:212974] dismissSettingsVC
2017-11-27 22:05:00.049177 app[998:211517] username not present

in this other case, it took 38 sec to dismiss the Settingsvc after receiving the login results.

EDITED I tried not using a vc presented. Instead in controller 1, i added a view that i first set as visible if username in defaults does not exist and then that I will hide after the login completion. in this view, i added a button to call the loginAction.

@IBAction func loginAction(sender: AnyObject) {
    if(isInternetAvailable()) {
        NSLog("internet available")

        login { (result) in
            switch result
            {
            case .Success(let users):
                print(users)
                self.loginView.isHidden = true
                NSLog("login ok: hiding view")
                break
            case .Failure(let error):
                print(error ?? "ERROR")
                break
            }
        }

    }
    else {
        NSLog("internet not available")
    }
}

Same result:

I see the completion and the received data:

2017-11-28 18:17:34.314706 cellar[1270:311710] username not present
2017-11-28 18:17:35.066333 cellar[1270:311710] internet available
2017-11-28 18:17:35.076930 cellar[1270:311710] done login
Optional({"cmd":"login","success":"true","message":"login succeded"})
2017-11-28 18:17:37.655829 cellar[1270:311763] login ok: hiding view

the view should be hidden before the NSLOG "login ok: hiding view". Instead, the UI is updated seconds after (about a min, but variable)

What would avoid the UI to be updated for so long as I wait the completion of the network stuff to perform the UI update?

UPDATE: weird situation: as soon as I get the network completion result, by changing the orientation, the dismiss appears right away:

Optional({"cmd":"login","success":"true","message":"login succeded"})
2017-11-28 22:28:30.620408 cellar[1461:360470] dismiss
2017-11-28 22:28:31.537588 cellar[1461:360413] username not present
2017-11-28 22:28:32.126759 cellar[1461:360413] [App] if we're in the 
real pre-commit handler we can't actually add any new fences due to CA 
restriction

your help is much that appreciated. Thanks


Solution

  • Not sure if this is the reason for your problem but it looks like you aren't running the dismiss() call on the main thread. You should call all UI code on the main thread. Wrap it as follows

         case .Success(let result):
            //print(result)
            DispatchQueue.main.async { 
                self.dismissSelf()
            }
         break