Explicitly specified type 'NSURL?' issue while updating to Swift 3 - swift

I am having issues while updating my iOS app's code to the latest version of Swift.
I have a function:
public class func gifWithURL(gifUrl:String) -> UIImage? {
// Validate URL
guard let bundleURL:NSURL? = NSURL(string: gifUrl)
else {
print("SwiftGif: This image named \"\(gifUrl)\" does not exist")
return nil
}
// Validate data
guard let imageData = NSData(contentsOf: bundleURL! as URL) else {
print("SwiftGif: Cannot turn image named \"\(gifUrl)\" into NSData")
return nil
}
return gifWithData(data: imageData)
}
And am getting a warning on the following line:
guard let bundleURL:NSURL? = NSURL(string: gifUrl)
and am getting the warning:
Explicitly specified type 'NSURL?' adds an additional level of optional to the initializer, making the optional check always succeed
Xcode allows me to fix the problem automatically. When I do this auto-fix, my code changes to:
guard let bundleURL:NSURL NSURL(string: gifUrl)
Which is obviously not the correct syntax.
I am unsure what I need to add/remove to get my code fully up to date with Swift 3 standards and working.

NSURL(string:) will return optional NSURL? instance and you are already optionally wrapping it with guard so remove the : NSURL? because you are setting it again optional instead of non-optional also, in Swift 3 use native URL and Data instead of NSURL and NSData. The whole code would be like.
guard let bundleURL = URL(string: gifUrl), let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: This image named \"\(gifUrl)\" does not exist")
return nil
}
//Access the imageData here
Note: Data(contentsOf:) will throws exception so you need to catch it using do try catch block.

You are doing it too complicated. In Swift 3, we don't use NSURL. It's just URL:
guard let bundleURL = URL(string: gifUrl) else {
print("SwiftGif: This image named \"\(gifUrl)\" does not exist")
return nil
}
Then you can also get rid of your dangerous force-cast:
guard let imageData = NSData(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(gifUrl)\" into NSData")
return nil
}

Related

Why is UIImage? nil here?

I'm trying to bridge React and Swift code by passing a string for an image path, which I've verified appears correctly on the Native side, and having a bit of an issue. The image path comes from React as NSString, and my goal is to pass that as a String to a Native function that will ultimately send data back to React.
Here's a snippet of some code that handles part of this
classifyImage(value as String)
and some of the body of the classifiyImage is as follows:
#objc func classifyImage(_ image: String) {
let imageData = Data(base64Encoded: image, options: .ignoreUnknownCharacters)!
let uiImage = UIImage(data: imageData)
guard let orientation = CGImagePropertyOrientation(
rawValue: UInt32((uiImage?.imageOrientation.rawValue)!)) else {
return
}
...code
}
The exact error is at the line with the rawVale, reading
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Here's more info if it may help...
Image data can come from the camera as such image NSMutableString "file:///var/mobile/Containers/Data/Application/54691469-2196-444E-9B45-C0D6F2CABEBC/Library/Caches/Camera/EEC3631C-3E96-44DA-B258-411363A2F10C.jpg" 0x00000002815a8420
or from the phone's gallery image String "ph://8F109DC0-CE95-4D0A-9D11-1B2E9CE6B8D3/L0/001"
Image from a file
First, we need to turn the string into a URL, then the URL into data like so:
let url = URL(string: image)
do {
let data = try Data(contentsOf: url)
} catch {
print(e)
}
Then we can use it to create the image.
do {
let data = try Data(contentsOf: url)
let image = UIImage(data: data)
guard let orientation = CGImagePropertyOrientation(
rawValue: UInt32(image.imageOrientation.rawValue)) else {
print("that didn't work")
return
}
} catch {
print(e)
}

data of image from url is nil after unwrapping

I'm using Swift 4 and trying to get images from my server. Using ipconfig in terminal, I know my localhost's ip is 10.43.229.215.
the following code is to retrieve the image data and turn it into UIImage:
func GetImage(url:String) {
let image = String(localIP + url).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let url = URL(string: image)
print(image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print("Client error!")
return
}
guard let data = data else {
print("Error: did not receive data")
return
}
DispatchQueue.main.async {
self.LensImageView.image = UIImage(data: data)
}
}).resume()
}
What I don't understand is that, the image string did show the image I want if I copy/paste the string to my browser
(http://10.43.229.215:3000/lensPic/%E5%A4%AA%E5%A6%83%E7%B3%96%E6%9D%8F.png)
However, error appears at the line self.LensImageView.image = UIImage(data: data) saying Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
I'm really confused about:
How can data be nil if there is already a guard let method?
Why can the data be nil is I can show the image through my browser?
Any help is highly appreciated!
The problem lies in making a false assumption about what is nil. It has nothing to do with the image or the data. What’s nil is self.LensImageView, the outlet property.

Display Image From Parse In Swift

I am trying to display the image I have stored in Buddy For Parse into a UIImageView, however I keep getting this error:
Could not cast value of type 'PFFileObject' (0x1045e0568) to 'NSString' (0x1041d75d8).
2019-04-13 18:15:09.869460-0500 PerfectLaptop[43839:3094232] Could not cast value of type 'PFFileObject' (0x1045e0568) to 'NSString' (0x1041d75d8).
I have already stored numerous strings into Parse, and am able to access them with no problems, and have stored the image I wanted to use also, however no matter what I try, I can't seem to get it to work. Many of the solutions I have found include casting the object as a PFFile, however this doesn't seem to exist anymore.
let query = PFQuery(className: "whichOneRecommended")
query.findObjectsInBackground { (objects, error) in
if error == nil
{
if let returnedobjects = objects
{
for object in returnedobjects
{
if (object["whichOne"] as! String).contains("\(self.whichOneJoined)")
{
self.laptopImage.image = UIImage(named: (object["laptopImage"]) as! String)
}
}
}
}
}
While the image file is viewable and downloadable in parse, I can't seem to actually have it be displayed in the imageview, and i want the image view to change programmatically by running this function as I have with other parts of the object.
Thanks in advance
The first thing to note is that PFFile has been renamed to PFFileObject.
You are trying to pass object["laptopImage"] which is a value of type Any to UIImage(named:) which can't be done because that function expects a String.
Firstly you need to create a constant of type PFFileObject:
let file = object["laptopImage"] as? PFFileObject
And then download the file data, create a UIImage from the PFFileObject and assign the image to the UIImageView:
file.getDataInBackground { (imageData: Data?, error: Error?) in
if let error = error {
print(error.localizedDescription)
} else if let imageData = imageData {
let image = UIImage(data: imageData)
self.laptopImage.image = image
}
}
Details on this can be found in the section on files in the iOS Guide.
guard let imageString = message,
let imageURL = URL(string: imageString) else { return }
do {
let imageData = try Data(contentsOf: imageURL)
image.image = UIImage(data: imageData)
} catch {
}

Why is Xcode complaining when I wrap this let in an if statement?

I have the following working code in my app:
func downloadJSON(completed: #escaping ([JsonFile.JsonBonuses]?) -> ()) {
let url = URL(string: "http://example.com/ExampleData.json")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil, let data = data {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)
self.defaults.set(posts.meta.version, forKey: "jsonVersion")
print("URLSession did not fail")
print("JSON Version Set to \(posts.meta.version)")
} catch {
print("Can't decode JSON: \(error)")
}
} else {
print("downloadJSON completed")
completed(nil)
}
}.resume()
}
I am wanting to change that URL to a different one based on a UserDefaults setting. So I wrapped the let url in an if statement like this:
if devModeStatus == true {
let url = URL(string: "https://otherexample.com/Example2Data.json")!
} else if devModeStatus == false {
let url = URL(string: "http://example.com/ExampleData.json")!
} else {
print("Invalid Dev Status encountered!")
return
}
However when I do that, Xcode complains about "Use of unresolved identifier 'url'; did you mean 'erfl'?" on the line that says URLSession.shared.dataTask(with: url) { (data, response, error) in
I'm not sure why it is complaining about this change. I use that same if/else logic else where to print a status message at first load of this view, so I know the variable is correct.
Your url declaration dies within those if, else-if scopes. You need to declare your url first then modify it. Also, since devModeStatus is a boolean value, the else case will never be reached, so no need for third path. Update your code as following:
let url: URL
if devModeStatus {
url = URL(string: "https://otherexample.com/Example2Data.json")!
} else {
url = URL(string: "http://example.com/ExampleData.json")!
}

How To Update A Label In Swift 3 With JSON Data Inside Of A Function?

For some reason whenever I try to update my label with the current temperature using self.infoLabel.text = String(temp!) inside of the DispatchQueue code block, I get the following fatal error message:
unexpectedly found nil while unwrapping an Optional value.
I'd appreciate if someone could help me figure out why the code below isn't working. Thanks.
func getCurrentTemp(city: String){
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
DispatchQueue.main.sync(execute: {
self.infoLabel.text = String(temp!)
})
//return temp as? String
//let temp_max = main["temp_max"] as? Double
//print("temp\(temp_max!)")
//let temp_min = main["temp_min"] as? Double
//print("temp\(temp_min!)")
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
There are two possibilities here: 1) either temp is nil (and it shouldn't be because you already force unwrap it in the print statement above) 2) or infoLabel is nil which happens if you broke your outlet connection.
Its easy to check; make a breakpoint above your assignment and in the debug console you can type:
po self.infoLabel
to see if its nil. For good measure you an also check temp.
You can also add a print statement to check self.infoLabel or an assert.
Alright, so I found a makeshift solution to this issue (See Below). Rather than placing the code inside of the function I made, I placed it in the viewDidLoad() function. For whatever reason, self.infoLabel? would be nil anywhere inside of the function I made.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("Sucessful launched weather page.")
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
var tempInFarenheit = ((9/5)*((temp!)-273) + 32).rounded()
DispatchQueue.main.sync(execute: {
self.infoLabel.text = "\(tempInFarenheit) + °"
})
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
Although this isn't the most effective way of doing things, hopefully it can help others who are having the same problem. If I find a more effective way of doing this, I'll be sure to edit this post and include it.