Search code examples
iosjsonswiftfacebookalamofire

unwrapping an Optional value


I am trying to login with macbook using code but I keep on getting this error:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

// API to log an user in
func login(userType: String, completionHandler: @escaping (NSError?) -> Void) {

    let path = "api/social/convert-token/"
    let url = baseURL!.appendingPathComponent(path)
    let params: [String: Any] = [
        "grant_type": "convert_token",
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "backend": "facebook",
        "token": FBSDKAccessToken.current().tokenString,
        "user_type": userType
    ]

    Alamofire.request(url!, method: .post, parameters: params, encoding: URLEncoding(), headers: nil).responseJSON { (response) in

        switch response.result {
        case .success(let value):

            let jsonData = JSON(value)

            self.accessToken = jsonData["access_token"].string!
            self.refreshToken = jsonData["refresh_token"].string!
            self.expired = Date().addingTimeInterval(TimeInterval(jsonData["expire_in"].int!))

            completionHandler(nil)
            break

        case .failure(let error):
            completionHandler(error as NSError?)
            break
        }
    }

}

The error is referring to this line:

self.accessToken = jsonData["access_token"].string!

and this is LoginViewController code:

import UIKit
import FBSDKLoginKit

class LoginViewController: UIViewController {
    @IBOutlet weak var bLogin: UIButton!
    @IBOutlet weak var bLogout: UIButton!

    var fbLoginSuccess = false
    var userType: String = USERTYPE_CUSTOMER

    override func viewDidLoad() {
        super.viewDidLoad()

        if (FBSDKAccessToken.current() != nil) {
            bLogout.isHidden = false
            FBManager.getFBUserData(completionHandler: {

                self.bLogin.setTitle("Continue as \(User.currentUser.email!)", for: .normal)
                // self.bLogin.sizeToFit()
            })
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        if (FBSDKAccessToken.current() != nil && fbLoginSuccess == true) {
            performSegue(withIdentifier: "CustomerView", sender: self)
        }
    }

    @IBAction func facebookLogout(_ sender: AnyObject) {
        APIManager.shared.logout { (error) in
            if error == nil {
                FBManager.shared.logOut()
                User.currentUser.resetInfo()

                self.bLogout.isHidden = true
                self.bLogin.setTitle("Login with Facebook", for: .normal)
            }
        }
    }

    @IBAction func facebookLogin(_ sender: AnyObject) {
        if (FBSDKAccessToken.current() != nil) {
            APIManager.shared.login(userType: userType, completionHandler:  { (error) in
                if error == nil {
                    self.fbLoginSuccess = true
                    self.viewDidAppear(true)
                }
            })
        } else {
            FBManager.shared.logIn(
                withReadPermissions: ["public_profile", "email"],
                from: self,
                handler: { (result, error) in
                    if (error == nil) {
                        FBManager.getFBUserData(completionHandler: {
                            APIManager.shared.login(userType: self.userType, completionHandler:  { (error) in
                                if error == nil {
                                    self.fbLoginSuccess = true
                                    self.viewDidAppear(true)
                                }
                            })
                        })
                    }
            })
        }
    }
}

Swift 3 Xcode 9 iOS 10.2

I read several texts to find out the causes of this type of error but not succeed.


Solution

  • First of all, it is a bad practice to use force unwrap (!) everywhere. Try to avoid it, until you really need it and/or know what are you doing. Here you use SwiftyJSON. It helps you to extract data from JSON in a convenient way. But you shouldn't rely, that you will always get proper JSON from the backend. There are many reasons why it can return wrong JSON and there would not be needed value. There are two options:

    1. You can use .stringValue instead of .string - it will return an empty string instead of nil
    2. You can do in this way: if let token = jsonData["access_token"].string {...} or even use guard statement

    Here is a good article to understand force unwrapping: https://blog.timac.org/2017/0628-swift-banning-force-unwrapping-optionals/