App store release crashing

So this may be because UserDefaults are getting reset after a period of time, maybe because I update the OS frequently, but a released version of my app after a period of time will crash on my login screen, probably where I check for saved credentials. Reinstalling the app fixes the issue. Console returns a lot of :

Unsupported use of UIKit view-customization API off the main thread. -setAlignsToKeyboard: sent to <_UIAlertControllerView: 0x125b2d070; frame = (0 0; 414 896); layer = <CALayer: 0x283f652e0>>

Cannot be called with asCopy = NO on non-main thread.

And my system logs show an error on this thread:

Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 4

Application Specific Information:
abort() called

...

Thread 4 name: Dispatch queue: com.apple.NSURLSession-delegate
Thread 4 Crashed:
0 libsystem_kernel.dylib 0x00000001bfb4e414 0x1bfb26000 + 164884
1 libsystem_pthread.dylib 0x00000001dd6a8b50 0x1dd6a6000 + 11088
2 libsystem_c.dylib 0x000000019b027b74 0x19afb1000 + 486260
3 libc++abi.dylib 0x00000001a6d1acf8 0x1a6d07000 + 81144
4 libc++abi.dylib 0x00000001a6d0be4c 0x1a6d07000 + 20044
5 libobjc.A.dylib 0x00000001a6c14f64 0x1a6c0e000 + 28516
6 libc++abi.dylib 0x00000001a6d1a0e0 0x1a6d07000 + 78048
7 libc++abi.dylib 0x00000001a6d1a06c 0x1a6d07000 + 77932
8 libdispatch.dylib 0x00000001917eddc4 0x1917ea000 + 15812
9 libdispatch.dylib 0x00000001917f510c 0x1917ea000 + 45324
10 libdispatch.dylib 0x00000001917f5c90 0x1917ea000 + 48272
11 libdispatch.dylib 0x00000001917ffd78 0x1917ea000 + 89464
12 libsystem_pthread.dylib 0x00000001dd6a9814 0x1dd6a6000 + 14356
13 libsystem_pthread.dylib 0x00000001dd6b076c 0x1dd6a6000 + 42860

Here is some code that I use to check saved credentials. In the backend API I cannot see any login attempts. If it helps, I can see this viewcontroller for a split second right before it crashes. Not sure if that rules out viewdidload.
Code Block
   override func viewDidLoad() {
    super.viewDidLoad()
    addLoadingSpinner()
    bLogIn.titleLabel?.font = UIFont.setToVoiceLight()
    self.tfCustomerID.delegate = self
    self.tfUsername.delegate = self
    self.tfPassword.delegate = self
    tfUsername.keyboardType = .default
    tfPassword.textContentType = .password
    setupUI()
      if UserDefaults.standard.string(forKey: UserDefaultsKeys.session) != nil
      && UserDefaults.standard.string(forKey: UserDefaultsKeys.savedCred) == "True" {
        login_session = UserDefaults.standard.string(forKey: UserDefaultsKeys.session)!
        check_session()
      }
else if UserDefaults.standard.string(forKey: UserDefaultsKeys.savedCred) == "True"
        && UserDefaults.standard.string(forKey: UserDefaultsKeys.usesBiometrics) == "True" {
        promptTouchOrFaceID()
      }
    removeLoadingSpinner()
  }
   override func viewWillAppear(_ animated: Bool) {
    AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
    cbSave.isChecked = UserDefaults.standard.string(forKey: UserDefaultsKeys.savedCred) == "True"
    addObservers()
  }
   func check_session() {
    UserDefaults.standard.set(login_session, forKey: "session")
    UserInfo.access_token = UserDefaults.standard.string(forKey: "session")!
     
    var request = URLRequest(url: NSURL(string: checksession_url)! as URL)
    request.httpMethod = .GET
    request.addValues(...)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
      if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
//NEVER GETTING TO API
        DispatchQueue.main.async {
...
        }
      } else {
        self.promptTouchOrFaceID()
        UserDefaults.standard.set(nil, forKey: UserDefaultsKeys.session)
      }
    }
    task.resume()
  }

Thanks in advance for any advice. Finding this to be pretty difficult to solve.
What do you mean line 40 : NEVER GETTING TO API

Do you go to line 45 instead ?
In that case, have you checked the value of statusCode ?

If you go line 45, what is
self.promptTouchOrFaceID()
Are you sure it does not access UI API and thus should run in main thread ?


I guess what I'm saying with that comment (not actually in the code) is that line 39 is never getting executed. I can confirm with the backend API. So no statusCode either. Here is my code for the biometrics prompt:
Code Block
func promptTouchOrFaceID() {
    //create context for touch auth
    let authenticationContext = LAContext()
    var error: NSError?
    authenticationContext.localizedFallbackTitle = ""
    let biometricType = authenticationContext.biometricType
    //check if the device has a fingerprint sensor, exit if not
    guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
      let title = "Error"
      var message = ""
      switch biometricType {
      case .faceID:
        message = "This device does not have a FaceID sensor or the sensor is not enabled."
      case .touchID:
        message = "This device does not have a TouchID sensor or the sensor is not enabled."
      case .none:
        message = "This device does not have any sensors to use TouchID or FaceID"
      }
       
      AlertService.showAlert(on: self, title: title, message: message)
      UserDefaults.standard.set("False", forKey: UserDefaultsKeys.usesBiometrics)
      removeLoadingSpinner()
      return
    }
    authenticationContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Please use your fingerprint to authenticate.", reply: { [unowned self] (success, error) -> Void in
      if (success) {
        //fingerprint recognized, set this possible and go to view controller
        OperationQueue.main.addOperation({ () -> Void in
          self.fillInUserDetails()
          self.login_now(username:self.tfUsername.text!, password: self.userDefaults.string(forKey: UserDefaultsKeys.password)!, customer: self.tfCustomerID.text!)
        })
      }
    })
  }


On line 22 I guess this is not a main dispatch, but wouldn't this cause a problem under normal use too? I am running on my own device and with a fresh install I get the correct prompt to use face id. I'm really suspicious of the UserDefaults since this happens only when the app isn't run for a long time.

Thanks
Could you instrument code to see what you get:

Code Block
let task = URLSession.shared.dataTask(with: request) { data, response, error in
print("response", response, type(of: response)
if let httpStatus = response as? HTTPURLResponse { print("status", httpStatus.statusCode) }
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {

sorry for the late reply. i can try replacing lines 38 with this but it won't even get to this point. I really think i have a misunderstanding of how UserDefaults works though and i'm finding out i cant look at the production version of my app's plist to see what's stored. I was thinking like on line 14 where i check to see if a UserDefault value is "True" I should check for nil first but this doesn't break when run as a fresh install and all of them are nil. The piece that makes this so difficult is I can't debug my app store version that is crashed and can't reproduce the error in development. Is there a difference in the way UserDefaults are stored in release vs. development?
App store release crashing
 
 
Q