Return empty URL on failure - swift

In a function like this:
func getMessageDetails()->URL{
if let theLinks = theMessage.links as? [[String:String]]{
let thisLink = theLinks[3]["Href"]
let url = URL(string: thisLink)
return url!
}
return nil// is unacceptable, what should I put here?
}
what should I return outside the closure?

In this case the best solution is to return an optional URL, it can also handle the case if the link is not a valid URL. And you should check if the thisLink array contains more than 3 items to avoid an out-of-range exception:
func getMessageDetails() -> URL? {
if let theLinks = theMessage.links as? [[String:String]],
theLinks.count > 3,
let thisLink = theLinks[3]["Href"] {
return URL(string: thisLink)
}
return nil
}

Related

Method to return value retrieved from HTTP request [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have a method which performs an HTTP request and retrieves data from a website, it's working as expected, I'm getting the data correctly. What I haven't been able to do is return the retrieved value when the method is called.
Here is the code...
func myFunction(zipCode: String)->String{
var myData:String = ""
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
return
}
myData = jsonArray[0]["MyPropertyName"]!
// Here, myData outputs, "Info for zip code 52484 from HTTP request"
}
task.resume()
return myData
}
When I call myFunction I get and empty string...
myFunction(zipCode: "52484")// Outputs an empty string
What I was expecting to see is a return value of "Info for zip code 52484 from HTTP request" since the myData variable was modified inside the let task = before the return call. I tried returning inside the let task = but this cannot be done in Swift.
How can I return the retrieved value when the myFunction is called?
You need a completion as request is asynchronous
func myFunction(zipCode: String,completion:#escaping(_ str:String?) -> () ) {
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
completion(nil)
return
}
guard let data = data else {
print("Data is empty")
completion(nil)
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
completion(nil)
return
}
let myData = jsonArray[0]["MyPropertyName"]!
completion(myData)
}
task.resume()
}
Call
myFunction(zipCode: "52484") { (str) in
if let st = str {
print(st)
}
}

Get header when enumerate an array

i try to get header informations about remote files which url are located in an array.
My function to get header works perfectly when called alone but doesn't when inside array enumerate.
Here my functions:
func getHeaderInformations (myUrl: URL, completion: #escaping (_ content: String?) -> ()) {
var request = URLRequest(url: myUrl)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil, let reponse = response as? HTTPURLResponse, let contentType = reponse.allHeaderFields["Content-Type"],let contentLength = reponse.allHeaderFields["Content-Length"]
else{
completion(nil)
return
}
let content = String(describing: contentType) + "/" + String(describing: contentLength)
completion(content)
}
task.resume()
}
// download picture
func downloadPic (){
let minimumSize=UserDefaults.standard.object(forKey: "Minimum_size") as! Int64
var imgExtension:String=""
var type:String=""
var size:Int64=0
for (_,item) in LiensImgArrayURL.enumerated() {
getHeaderInformations(myUrl: item, completion: { content in
let myInfoArray = content?.components(separatedBy: "/")
type=(myInfoArray?[0])!
imgExtension=(myInfoArray?[1])!
size=Int64((myInfoArray?[2])!)!
})
if (type=="image" && size>=minimumSize) {
SaveFileToDirectory(myRemoteUrl: item, myExtension: imgExtension)
}
}
}
How can i write well this code for "getHeaderInformations" works and return good values inside the func "downLoadPic"?
Thanks for your answers...
Since getHeaderInformations works asynchronously put the code to save the file in the closure.
If you don't need the index in the for loop you don't need enumerated() either.
I tweaked the code bit to get rid of ugly question and exclamation marks and parentheses.
for item in LiensImgArrayURL {
getHeaderInformations(myUrl: item, completion: { content in
if let myInfoArray = content?.components(separatedBy: "/") {
type = myInfoArray[0]
imgExtension = myInfoArray[1]
size = Int64(myInfoArray[2])!
if type == "image" && size >= minimumSize {
SaveFileToDirectory(myRemoteUrl: item, myExtension: imgExtension)
}
}
})
}

Access a dictionary (JSON format) in a function with a flexible variable

I can't find a solution for my programming issue. I want to create a function which will access a dictionary (data is coming from the internet) an I need the following code very often:
if let job_dict = json["data"] as? [String:Any] {
It would be great to be more flexible and to change the ["data"] part to a variable or something like that:
func get_JSON(Link: String, Value: String) -> [Double] {
let url = Link
let request = URLRequest(url: URL(string: url)!)
let myUrl = URL(string: basePath)!
var ValuestoReturn = [Double]()
let task = URLSession.shared.dataTask(with: myUrl) { (data, response, error) in
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
print(Value.description)
if let job_dict = json[Value.description] as? [String:Any] {
print(job_dict)
}
}
} catch {
}
}
}
task.resume()
json[Value.description] is always wrong and the json["data"] thing is always true.
Don't use Value.Description use just Value
print(Value)
if let job_dict = json[Value] as? [String:Any] {...}
P.D: Don't use "Value" for a variable's name. The first letter in uppercase is for types. You can use value instead.

Is there a shorthand for guard return in swift?

Is there is a way to have guard automatically return without needing to actually write it out every single time, e.g:
guard let url = self.webView.url else { return }
guard let componentDict = URLComponents(string: url.absoluteString)?.dict else { return }
guard let id = componentDict["v"] else { return }
guard let idUrl = URL(string: baseUrl + id) else { return }
In the case where I actually need to do something in addition to return, I would include the else { return } bit with my extra handling.
Its not a huge bother, but it would be a nice thing to have.
guard statement is typed with else must be, there is no shortcut for this but you can use if..let to avoid else statement.
Or combine this related variables statement with single guard statement
guard let url = webView.url,
let componentDict = URLComponents(string: url.absoluteString)?.dict,
let id = componentDict["v"],
let idUrl = URL(string: baseUrl + id)
else { return }
You could write that code using if let:
if let url = self.webView.url,
let componentDict = URLComponents(string: url.absoluteString)?.dict,
let id = componentDict["v"],
idUrl = URL(string: baseUrl + id) {
// do something with idURL
} else {
return // if needed
}
But in short, no, you can't shorten an individual guard ... else { return }.

Optional unwrapping for class properties Swift

Let's say I have the following class definition:
class Request {
let url: NSURL?
init?(url: String) {
guard let self.url = NSURL(string: url) else {
self.url = nil
return nil
}
}
}
The guard statement doesn't work so I'm having to do the following:
let url: NSURL?
init?(url: String) {
guard let _ = NSURL(string: url) else {
self.url = nil
return nil
}
self.url = NSURL(string: url)!
}
Which feels quite long - is there another, simpler way to achieve what I'm trying to achieve which is to create a failable initialiser whose only property takes a value from NSURL (which returns an optional). The initialiser is to fail and return nil if the NSURL value returns nil.
Your first version could work, you just forgot to give guard a variable name for the unwrapped value (you used self.url instead, that's the mistake):
class Request {
let url: NSURL?
init?(url: String) {
guard let value = NSURL(string: url) else {
self.url = nil
return nil
}
self.url = value
}
}