I have Medicine struct that holds image_origin URL of image I want to download and set to the ImageView.
For downloading/caching purposes I'm using Kingfisher framework.
let m = medicines[indexPath.row]
cell.medicineImage.kf.setImage(with: URL(string: m.value(forKeyPath: "image_origin") as! String)!)
cell.medicineName.text = m.value(forKeyPath: "i_name") as? String
In the code above m is an NSManagedObject of CoreData. I try to get image URI from the CoreData and set it to the ImageView, but every time at the line 2 I get the following error message: Unexpectedly found nil while unwrapping an Optional value
I have tried changing variables and Optinal types, tried to hardcode URI but without success.
What am I doing wrong?
P.S. Im using Swift4
Just unwrap safely to fix the crash and check your database if you are not getting the urlString properly,
if let urlString = m.value(forKeyPath: "image_origin") as? String {
print(urlString)
guard let url = URL(string: urlString) else { return }
cell.medicineImage.kf.setImage(with: url)
}
There might be a problem with image_origin key that I may have value as string or may not have. So, just need to confirm the value and use it
let m = medicines[indexPath.row]
cell.medicineName.text = m.value(forKeyPath: "i_name") as? String
guard let urlPath = m.value(forKeyPath: "image_origin") as? String, let url = URL(string: urlPath) else { return }
cell.medicineImage.kf.setImage(with: url)
Related
Essentially I am parsing JSON data and assigning it to a variable called addressPressNow I then have the following function that executes when a user taps on a UILabel:
The goal is to have Apple Maps open provided the variable value it contains.
Because I am assigning an address to a variable it will contain spaces
ex: 3981 Test Drive Cupertino CA 95014
NOTE: The value of the variable is being passed correctly because when I do print(addressPressNow) in func tapFunction it prints correctly.
#objc
func tapFunction(sender:UITapGestureRecognizer) {
let targetURL = NSURL(string: "http://maps.apple.com/?q=" + addressPressNow)!
UIApplication.shared.openURL(targetURL as URL)
}
The issue is I am having trouble applying the variable to the string URL with the following error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
The following is how I am assigning the value to the variable:
struct FacilityInfo: Decodable {
let address: String
class infoViewController: UIViewController {
var addressPressNow : String = ""
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(infoViewController.tapFunction))
addressInfo.isUserInteractionEnabled = true
addressInfo.addGestureRecognizer(tap)
let url = URL(string: "https://test/test/example”)!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// ensure there is no error for this HTTP response
guard error == nil else {
print ("error: \(error!)")
return
}
// ensure there is data returned from this HTTP response
guard let data = data else {
print("No data")
return
}
// Parse JSON into array of Car struct using JSONDecoder
guard let cars = try? JSONDecoder().decode([FacilityInfo].self, from: data), let secondCar = cars.first
else {
print("Error: Couldn't decode data into cars array")
return
}
DispatchQueue.main.async {
self.addressPressNow = secondCar.facility_address
}
}
"I am assigning an address to a variable it will contain spaces"
If the address contains spaces then creating NSURL with the string will crash. You can use addingPercentEncoding to solve the problem
if let encodedAddress = addressPressNow.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
let targetURL = NSURL(string: "http://maps.apple.com/?q=" + encodedAddress)!
UIApplication.shared.openURL(targetURL as URL)
}
And don't use NSURL and force unwrapping. Update it like this
if let encodedAddress = addressPressNow.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let targetURL = URL(string: "http://maps.apple.com/?q=" + encodedAddress) {
UIApplication.shared.openURL(targetURL)
}
As suggested by matt use URLComponents
let addressPressNow = "3981 Test Drive Cupertino CA 95014"
var components = URLComponents(string: "http://maps.apple.com")
components?.queryItems = [URLQueryItem(name: "q", value: addressPressNow)]
print(components?.url)//http://maps.apple.com?q=3981%20Test%20Drive%20Cupertino%20CA%2095014
if let targetURL = components?.url {
UIApplication.shared.open(targetURL, options: [:], completionHandler: nil)
}
You are saying
NSURL(string: "http://maps.apple.com/?q=" + addressPressNow)!
Notice the exclamation mark at the end. That means "if there's a problem, crash me". You can hardly complain if you do in fact crash; that is what you asked to do.
Basically, never use NSURL(string:) if you can avoid it. To form a valid URL, build it up using URLComponents. And form it out of valid components. (It is impossible to say whether facility_address is a valid URL query, because you have not shown what it is.)
Example:
var comp = URLComponents()
comp.scheme = "https"
comp.host = "maps.apple.com"
comp.queryItems = [URLQueryItem(name: "q", value: "1 Infinite Loop, Cupertino, CA")]
if let url = comp.url {
print(url) // https://maps.apple.com?q=1%20Infinite%20Loop,%20Cupertino,%20CA
}
That gives us a valid URL that actually works.
I am having and imageView and I am getting the userData from the server when loggedIn in the userData I have a parameter for "profilePic":"penguins.jpg". now I am adding the domain like "http://.....(self.userData.value(forKey: "profilePic")!)" and saving this value into a string variable to store and when I am accessing the value and trying to convert the url into data and adding to imageView.image it is throwing : unexpectedly found nil while unwrapping an optional value..
All my other userData like name,address,phoneNumber are showing fine except for Image.
My Code:
here are the some of the many ways I tried:
way:1
let ad : AppDelegate = UIApplication.shared.delegate as! AppDelegate
let imageUrlString = ad.userImagePath
let imageUrl:URL = URL(string: imageUrlString)! // it is throwing error here(: unexpectedly found nil while unwrapping an optional value)
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
self.profileImage.image = image
}
}
way:2
if let url = NSURL(string: ad.userImagePath) {
if let data = NSData(contentsOf: url as URL){
if let imageUrl = UIImage(data: data as Data) {
profileImage.image = imageUrl
}
}
}
I have tried different ways to solve this, can't figure out what is my mistake.. finally I am here.. Please someone help he...
I want to create URL from string in dictionary, but when I initialize it, the guard statement goes to else statement, because values["Image"] is nil and return the function. Here is a piece of my code
guard let imageURLInDatabase = URL(string: values["Image"]!) else { return }
let data = NSData(contentsOf: imageURLInDatabase)
I have create afew breakpoint in my code:
And here is my console log at this breakpoint:
What did I miss?
P.S
I tried to do it like:
let imageURLInDatabase = URL(string: values["Image"]!)
but I got error message unexpectedly found nil while unwrapping an optional value
Edit 1:
I tried to do it like
guard let imageString = values["Image"] else{return}
guard let imageURLInDatabase = URL(string: imageString) else{
return}
let data = NSData(contentsOf: imageURLInDatabase)
And set breakpoints:
So values["Image"] can't be nil
Edit 2:
This is what is in values["Image"]:
some : "https://firebasestorage.googleapis.com/v0/b/blackjack-ef580.appspot.com/o/profiles_images%2FdwMGR8sKMvYauf6uQDtyop18cwy1.jpg?alt=media&token=5df48794-d3fd-4e5c-a72c-9928a8a43c4e\""
The problem is the extra quotation mark at the end of the URL string. Try this:
let s = "https://firebasestorage.googleapis.com/v0/b/blackjack-ef580.appspot.com/o/profiles_images%2FdwMGR8sKMvYauf6uQDtyop18cwy1.jpg?alt=media&token=5df48794-d3fd-4e5c-a72c-9928a8a43c4e\""
let url = URL(string:s)
You'll get nil.
The problem isn't that values["Image"] is nil. The problem is that values["Image"] isn't a value URL string.
Make sure you properly encode spaces and other special characters in the URL.
And as a side note, do not use NSData, use Data.
According to your trace, the problem isn't that value["Image"] is nil. If that were the case, you would have crashed rather than called return. Your problem is that whatever the value of value["Image"], it is not a valid URL. Without seeing the value, it's difficult to know exactly what is wrong with it, but it is malformed in some way.
This is because the dictionary values does not contains the key Image. So values['Image'] will return nil, and force unwrapping a nil will crash the app.
Try using guard else statements to unwrap it
guard let imageValue = values["Image"] as? String else {
//imageValue is nil here, handle accordingly
return
}
guard let imageURLInDatabase = URL(string: imageValue) else {
//imageURLInDatabase is nil here, handle accordingly
return
}
let data = NSData(contentsOf: imageURLInDatabase)
Edit 1:
With your updated question, and the breakpoint image, values["Image"] is not null, imageURLInDatabase is nil due to which it goes to else case.
The reason could be that values["Image"] might not be returning a valid url, due to which URL(string: ..) fails.
Edit 2 :
Check the string url you just shared
"https://firebasestorage.googleapis.com/v0/b/blackjack-ef580.appspot.com/o/profiles_images%2FdwMGR8sKMvYauf6uQDtyop18cwy1.jpg?alt=media&token=5df48794-d3fd-4e5c-a72c-9928a8a43c4e\""
has an extra quote in the end!
ok guys can I get a little help with my code. When running the app I get an error is there any way to fix this problem?
let fileUrl = dict["fileUrl"]as! String
let url = NSURL(string: fileUrl)
let data = NSData(contentsOf: url! as URL!)
let picture = UIImage(data: data! as Data!)
let photo = JSQPhotoMediaItem(image: picture)
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photo))
Image here
Here I see 4 big fat problems with your code.
You are force casting the value of fileUrl of the dictionary dict to String. If your dictionary doesn't have the value for fileUrl, or if it's not castable to string, your code will crash. You should change that to optional cast like:
if let fileUrl = dict["fileUrl"] as? String
{
//your code if you have fileUrl
}
When creating the url to the file, you are using the wrong initialization method, you should be using this:
let url = URL(fileURLWithPath: fileUrl)
After you have the url to the file, you should also check if you have the data of the file, because contentsOfFile: initializer of the NSData returns the optional object, which may be nil, so another if check:
if let data = NSData(contentsOf: url) {\\ code with the data}
init?(data: Data) initializer of the UIImage also returns optional object, so if the required by latter code, you should also check if you have the image or nil with if statement.
The result code should be something like:
if let fileUrl = dict["fileUrl"] as? String {
let url = URL(fileURLWithPath: fileUrl)
if let data = NSData(contentsOf: url) {
let image = UIImage(data: data as Data) // you can cast NSData to Data without force or optional casting
let photo = JSQPhotoMediaItem(image: image)
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photo))
}
}
Hope this helps.
Replace the first line of code with this line for optional binding check :-
guard let fileUrl = dict["fileUrl"] as! String else {return}
Yo should do validation in cases where the variable may be nil, the following is an example:
if let fileUrl = dict["fileUrl"] as? String {
let url = URL(string: fileUrl)
do {
let data = try Data(contentsOf: url!)
let picture = UIImage(data: data)
let photo = JSQPhotoMediaItem(image: picture)
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photo))
} catch {
}
}
Now apple have handily gotten rid of the NSString and String automatic compatibility, I'm having a bit of a nightmare going between the two. I'm getting a couple of NSStrings out of a dictionary and I can't convert them to regular Strings...
I've tried:
let fileNameString: String = String(format: "%#", filename!)
let fileNameString: String = (filename as! String)
let fileNameString = filename as? String
let fileNameString = (filename as? String) ?? ""
if let fileNameString = filename as? String {
println("\(fileNameString)")
}
But all produce the error.
I've broken in at the point of conversion and can see neither NSStrings are nil:
But no joy with either. Getting Thread 1: EXC_BAD_ACCESS (code=1, address=0x20) . Am I missing something obvious here?
Even just trying to print the NSString filename before conversion causes the same error..
Posting the code prior to conversion attempt to see if that has anything to do with it...
// First we create a head request as the info I need is in the headers
var newRequest: NSMutableURLRequest = NSMutableURLRequest(URL: request.URL!)
newRequest.HTTPMethod = "HEAD"
var response: NSURLResponse?
NSURLConnection.sendSynchronousRequest(newRequest, returningResponse: &response, error: nil)
// Unwrap response as httpResponse in order to access allHeaderFields
if let httpResponse = response as? NSHTTPURLResponse {
let headerString = "sfn-Document-Filename"
let headerNSString = headerString as NSString
let filetypeString = "Content-Type"
let filetypeNSString = filetypeString as NSString
// This is a dictionary where the keys are NSCFStrings
// (NSStrings, hence creating the NSStrings above)
var allHeaders = httpResponse.allHeaderFields
// Getting the filename out here only works with as? NSString. as? String creates the same error as converting.
let filename = allHeaders[headerNSString] as? NSString
// This is a string which contains the type as 'application/pdf' for example. We only need the part after the /.
// Again, trying to get this out as a String fails
let typeString = allHeaders[filetypeNSString] as? NSString
var typeArray = typeString?.componentsSeparatedByString("/") as! [NSString]
let filetype = typeArray[1]
}
If this were an NSString, then all you’d need to do is filename as String (no !). But it sounds like the problem is your filename, of optional type NSString?, is nil. (option-click filename to confirm its type)
If there’s a reasonable default (say, an empty string), try
let fileNameString = (filename as? String) ?? ""
Or if you need to handle the nil with specific code:
if let fileNameString = filename as? String {
// use fileNameString, which will be unwrapped and of type String
}
else {
// log error or similar
}
Or, if you want to defer unwrapping, but want to change the type inside the possible value, you can do
let fileNameString = filename as? String
// which is a less long-winded way of saying
let fileNameString = filename.map { $0 as String }
Generally speaking, you should try and cut down on the ! usage, since it leads to problems like this. ! is only for those times when you know from the code that the value absolutely positively cannot be nil.
edit: based on your sample code, try the following:
let url = NSURL(string: "http://www.google.com")
let request = url.map { NSMutableURLRequest(URL: $0) }
request?.HTTPMethod = "HEAD"
let response: NSHTTPURLResponse? = request.flatMap {
var response: NSURLResponse?
NSURLConnection.sendSynchronousRequest($0, returningResponse: &response, error: nil)
return response as? NSHTTPURLResponse
}
let headers = response?.allHeaderFields as? [String:String]
// google.com has no such header but still...
let filename = headers?["sfn-Document-Filename"]
// bear in mind someArray[1] will also crash if there's no such entry,
// first/last are better if that's what you want
let type = headers?["Content-Type"]?
.componentsSeparatedByString(";").first?
.componentsSeparatedByString("/").last
Everything here is optional, but safe, so you can test at various points for nil for logging/error reporting purposes.