I'm trying to make some connections with an API and I need to return an error message if the username is unknown. Though, while I try to print my variable in my post request, I see the message but if I print my variable after my request but in my function, I have an error: fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb).
Here is a sample of my code. I use SwiftHTTP for my requests:
var errSign: String?
func signUp(email:String, pwd:String) {
let params: Dictionary<String,AnyObject> = ["password": pwd, "email": email]
task.POST(signUpUrl, parameters: params, completionHandler: {(response: HTTPResponse) -> Void in
if let err = response.error {
println("error: \(err.localizedDescription)")
}
if let json = response.responseObject as? Dictionary<String, AnyObject> {
var data = JSON(json)
if data["error"] != false {
self.errSign = String(stringInterpolationSegment: data["error"])
println(self.errSign!)
}
}
})
// ERROR println(self.errSign!)
}
You've declared errSign as optional, so you should probably check to see that there's something in there before attempting to print anything out:
self.errSign = String(stringInterpolationSegment: data["error"])
if let actualErrSign = self.errSign
{
println(\(actualErrSign))
} else {
println("no (obvious) error was returned")
}
the ! keyword is to unwrap an optional variable to access to its value. This is only possible when your optional variable contains a value. In case you try to unwrap an optional variable that does not hold actually a value, your application will crash and it receive that fatal error.
The solution is in the answer of #Michael Dautermann
Related
I am trying to access the .localizedDescription property through Alamofire's callback in the event that there is an error
I have an enum which handles the Error types specifically similar to the Alamofire docs
enum BackendAPIErrorManager: Error {
case network(error: Error)
case apiProvidedError(reason: String) // this causes me problems
...
}
In my request, if there is an error I store the applicable error to the .failure case Alamofire provides as follows:
private func resultsFromResponse(response: DataResponse<Any>) -> Result<Object> {
guard response.result.error == nil else {
print(response.result.error!)
return .failure(BackendAPIErrorManager.network(error: response.result.error!))
}
if let jsonDictionary = response.result.value as? [String: Any], let errorMessage = jsonDictionary["error"] as? String, let errorDescription = jsonDictionary["error_description"] as? String {
print("\(errorMessage): \(errorDescription)")
return .failure(BackendAPIErrorManager.apiProvidedError(reason: errorDescription))
}
....
}
where that method is called in:
func performRequest(completionHandler: #escaping (Result<someObject>) -> Void) {
Alamofire.request("my.endpoint").responseJSON {
response in
let result = resultsFromResponse(response: response)
completionHandler(result)
}
}
Then when I call the performRequest(:) method, I check for the error in the completion handler:
performRequest { result in
guard result.error == nil else {
print("Error \(result.error!)") // This works
//prints apiProvidedError("Error message populated here that I want to log")
print(result.error!.localizedDescription) // This gives me the below error
}
}
In the event of an error where .failure(BackendAPIErrorManager.apiProvidedError(reason: errorDescription)) is returned, I receive the below error
The operation couldn’t be completed. (my_proj.BackendErrorManager error 1.)
How would I get the String value from the returned Error? I have tried .localizedDescription to no luck. Any help would be great!
To be able to call .localizedDescription on your custom BackendAPIErrorManager conforming to the Error protocol, you will need to add this code:
extension BackendAPIErrorManager: LocalizedError {
var errorDescription: String? {
switch self {
case .apiProvidedError:
return reason
}
}
}
The reason is a bit foggy (as the documentation on how the Error protocol works is not too detailed yet) but basically, in Swift 3, to be able to provide localized error descriptions (and other relative info), you need to conform to the new LocalizedError protocol.
You can find more details here:
How to provide a localized description with an Error type in Swift?
Sorry for my dummy question but I'm new on Swift development, I have a project on Swift 1.1 which was working properly but after upgrading to Xcode to 7.3.1 (With Xcode 2.2.1) my project is built successfully but I'm getting an error while running it (on the let request line) with the following code:
// Send HTTP GET Request
let request = NSURLRequest(URL: NSURL(string: "http://11.22.33.44:8080/MySRV/login?email=\(emailField.text)&password=\(pwdField.text)")!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{
(response: NSURLResponse?, data: NSData?, error: NSError?)-> Void in
print("response \(response?.description)")
the error is:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
fatal error: unexpectedly found nil while unwrapping an Optional value
I can imagine that my let request is nil but I don't know how to solve it.
Thanks for your help.
From here it looks like your unwrapping an optional value that is nil. Try this:
if let url: NSURL = NSURL(string: "http://11.22.33.44:8080/MySRV/login?email=\(emailField.text)&password=\(pwdField.text)") {
let request = NSURLRequest(URL: url)
// Do asynchronous request here
} else {
// NSURL is incorrect
}
This is how you can safely unwrap an optional value in Swift. You might also want to check if the emailField.text and pwdField.text are also not nil values. It would look something like this:
if let email: String = emailField.text {
// Text is not nil
} else {
// UITextField text is nil!
}
In the else blocks you can add logic to notify the user to input text or to perform some other action. You can read more about this here.
Hope this helps!
The problem is likely a result of the fact that the text properties of your two text fields are optionals, and thus is being added to your URL as Optional("user#domain.com") and Optional("password"), and thus the attempt to build the URL is failing.
You can do something like:
guard let email = emailField.text else {
print("email is `nil`")
return
}
guard let password = pwdField.text else {
print("password is `nil`")
return
}
let request = NSURLRequest(URL: NSURL(string: "http://11.22.33.44:8080/MySRV/login?email=\(email)&password=\(password)")!)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I get the error "Fatal error: Unexpectedly found nil while unwrapping an optional value". I am trying to fetch JSON as a dictionary from my server. How do I throw the error if the data is nil?
let jsonUrl = "jsonurl"
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!, completionHandler: {
(data,response,error) -> Void in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) // Get error
dispatch_async(dispatch_get_main_queue()) {
for newData in json as! [Dictionary<String, AnyObject>] {
// do stuff
}
}
} catch {
}
})
task.resume()
Edit: to clarify, I am testing when there is no internet connection, it should ignore the error thrown, instead it gives an error. I tried
guard let data = data else { return }
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
but it says "Cannot force unwrap of optional type 'NSData'" on the let json line
Instead of guard the data parameter handle the error returned in the completion handler.
If there is no error then data can be safely unwrapped.
let task = session.dataTaskWithURL(shotsUrl!, completionHandler: {
(data,response,error) -> Void in
if error != nil {
// do proper error handling
} else {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
...
And add also appropriate error handling in the catch scope. It seems to be annoying but can be very useful. ;-)
You are force unwrapping data by saying data!. This means if it expects data not to be nil, which it will always be if there is no internet connection. You at least need to check data is nil before you force unwrap, and then handle how you want your object created if it is nil.
if data != nil {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) // Get error
dispatch_async(dispatch_get_main_queue()) {
for newData in json as! [Dictionary<String, AnyObject>] {
// do stuff
}
}
} catch {
}
} else {
//handle object creation if data is nil
}
I'm not sure where the nil value is here. I also tried explicitly allocating the error var and still had the issue. The code is taken almost directly from Twitter documentation.
let statusesShowEndpoint = "https://api.twitter.com/1.1/users/profile_banner.json?"
let params = ["screen_name":"twitter"]
var clientError :NSError?
let request = Twitter.sharedInstance().APIClient.URLRequestWithMethod(
"GET", URL: statusesShowEndpoint, parameters: params,
error: &clientError)
if request != nil {
Twitter.sharedInstance().APIClient.sendTwitterRequest(request) {
(response, data, connectionError) -> Void in
if (connectionError == nil) {
var jsonError : NSError?
let json : AnyObject? =
NSJSONSerialization.JSONObjectWithData(data,
options: nil,
error: &jsonError)
}
else {
println("Error: \(connectionError)")
}
}
}
else {
println("Error: \(clientError)")
}
I get this error on the request:
fatal error: unexpectedly found nil while unwrapping an Optional value
I haven't spent much time with the twitter api, but the http endpoint you are trying to access requires that you have the qAuth authority have you done this? as the http link is coming back "bad authentication data" when i try to access it
I am attempting to pull information from a JSON on my website. In doing so, if there is an error with the connection, it should return that error and log it to the console. The problem I am running into is that if I turn on Airplane more or otherwise lose signal, the error: fatal error: unexpectedly found nil while unwrapping an Optional value crashes the application. Why is it returning this, when I have set conditions for the error to simply be logged? Thank you in advance!
I am unsure why the error is not being logged and preventing the application from crashing. Any pointers would be helpful.
JSONLoader.swift
import Foundation
var arrayOfMeals: [Meal] = [Meal]()
var weekDayArray = ["monday"]
func getJSON(urlToRequest: String) -> NSDictionary {
var url: NSURL = NSURL(string: urlToRequest)!
var jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil
var error: NSError?
var dataValue: NSData = NSURLConnection.sendSynchronousRequest(jsonRequest, returningResponse: jsonResponse, error:&error)!
if error? == nil {
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataValue, options: NSJSONReadingOptions.MutableContainers , error: &error) as NSDictionary
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
if error? == nil {
return jsonResult
}
else {
return NSDictionary(object: "Error: Something with parsing went wrong :(", forKey: "error")
}
}
else {
return NSDictionary(object: "Error: There was an error with your connection :(", forKey: "error")
}
}
func loadJSON(jsonDictionary: NSDictionary) {
for days in weekDayArray{
var resultsArray = jsonDictionary[days] as NSArray
for obj: AnyObject in resultsArray{
let breakfast = (obj.objectForKey("breakfast")! as String)
let lunch = (obj.objectForKey("lunch")! as String)
let dinner = obj.objectForKey("dinner")! as String
let dateString = obj.objectForKey("dateString")! as String
let dayOfWeek = obj.objectForKey("dayOfWeek")! as String
let newMeal = Meal(breakfast: breakfast, lunch: lunch, dinner: dinner, dayOfWeek: dayOfWeek, dateString: dateString)
if theDays(newMeal.dateString) >= 0 {
arrayOfMeals.append(newMeal)
}
}
}
}
I expect the function getJSON to establish an NSURLConnection to the site, parse the JSON date, and return an NSDictionary. An error case is established if there is an error in the connection, then it returns the dictionary with a value explaining the error. An additional error case occurs to parse the JSON file, and returns an NSDictionary with similar error.
I expect the function loadJSON to create instances of an object Meal, which I define as having properties of breakfast, lunch, dinner, a date, and a day of the week. The values of this instance are the results of the returned NSDictionary from function getJSON. If the day is the future, append it to my arrayOfMeals. Otherwise, ignore the instance.
MealViewController
override func viewDidLoad() {
super.viewDidLoad()
var req = getJSON("http://www.seandeaton.com/meals/Mealx")
loadJSON(req)
}
MealModel.swift - to create instances of the meals
class Meal {
let breakfast: String
let lunch: String
let dinner: String
let dayOfWeek: String
let dateString: String
init(breakfast: String, lunch: String, dinner: String, dayOfWeek: String, dateString: String) {
self.breakfast = breakfast
self.lunch = lunch
self.dinner = dinner
self.dayOfWeek = dayOfWeek
self.dateString = dateString
}
}
How can I prevent the app from crashing upon failure to establish a connection and log the error message to the console? Thanks again.
The key observation is that in the call to sendSynchronousRequest in which you're retrieving the NSData, you have defined NSData to not be optional, and you appended a ! which will force the unwrapping of the optional return value. Thus if the optional NSData was nil (which would happen if there were any network problems), this code would fail.
Instead, you should simply leave the NSData as an optional (i.e. a NSData?) and check whether it was nil or not before trying to unwrap it. Thus, I might suggest something like:
func retrieveJSON(urlToRequest: String) -> NSDictionary {
let url: NSURL = NSURL(string: urlToRequest)!
let jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: NSURLResponse?
var error: NSError?
let dataValue = NSURLConnection.sendSynchronousRequest(jsonRequest, returningResponse: &jsonResponse, error:&error)
if dataValue != nil {
if let jsonResult = NSJSONSerialization.JSONObjectWithData(dataValue!, options: .MutableContainers, error: &error) as? NSDictionary {
return jsonResult
} else {
return [
"error": "Error: Something with parsing went wrong :(",
"localizedDescription": error?.localizedDescription ?? ""
];
}
}
else {
return [
"error": "Error: There was an error with your connection :(",
"localizedDescription": error?.localizedDescription ?? ""
];
}
}
The above will fix crashes resulting from the forced unwrapping of the optional NSData, which would have been be nil if there were networking problems. Note, you don't show us the code that calls getJSON and then subsequently calls loadJSON, but I assume you're doing the necessary checking for error handling there.
Note, I also retired the awkward AutoreleasingUnsafeMutablePointer<NSURLResponse?> construct. I also added the localizedDescription of the error to the dictionary being returned.
Personally, I'd generally return the complete NSError object and the NSURLResponse objects, so that the caller can diagnose the precise nature of the error, rather than just text descriptions.
In a more radical edit to your code, I'd suggest you generally avoid synchronous requests. Never perform synchronous requests from the main thread.
For example, you might define the method to perform the request asynchronously, like so:
func retrieveJSON(urlToRequest: String, completionHandler:(responseObject: NSDictionary?, error: NSError?) -> ()) {
let url: NSURL = NSURL(string: urlToRequest)!
let jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: NSURLResponse?
NSURLConnection.sendAsynchronousRequest(jsonRequest, queue: NSOperationQueue.mainQueue()) {
response, data, error in
if data == nil {
completionHandler(responseObject: nil, error: error)
} else {
var parseError: NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &parseError) as NSDictionary?
completionHandler(responseObject: jsonResult, error: error)
}
}
}
You'd then call it, using the "trailing closure syntax", like so:
retrieveJSON(urlString) {
responseObject, error in
if responseObject == nil {
// handle error here, e.g.
println(error)
return
}
// handle successful the `NSDictionary` object, `responseObject`, here
}
// but don't try to use the object here, because the above runs asynchronously