Repeat function with general command (without mentioning function name) - ios

How do I repeat a swift function without naming that functions name?
Like if I have this function:
func repeatAtFail(){
let hasFailed = Bool.random()
if(hasFailed){
//repeat this function without calling function name
}
print("function succeed")
}
So I am looking for a general command to repeat a function. Just like "Return". But Return exits the function and I need one command that repeats it.
EDIT 11:08
This is the function I have
func loadRun(){
let request = get_request(postString: "function_name=get_run&userid=\(userid)&token=\(token!)")
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
guard let data = data else {
self.showErrorMessage()
//HERE I NEED TO REPEAT THE FUNCTION
throw JSONError.NoData
}
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary else {
self.showErrorMessage()
throw JSONError.ConversionFailed
}
if(checkErrors(json: json)){
self.showErrorMessage(message:json["error"] as! String)
return
}
self.data = json["payload"] as! [[String: Any]]
DispatchQueue.main.sync {
self.loadingBar.stopAnimating()
}
} catch let error as JSONError {
self.showErrorMessage()
print(error.rawValue)
} catch let error as NSError {
self.showErrorMessage()
print(error.debugDescription)
}
}.resume()
}
Occasionally it occurs that I get error -999 which is probably due to a failure of the SSL certificate on the domain. Till that issue is fixed I need a solution to reload the function so the users don't face any issues.

Related

Networking Layer in Swift , Completion Blocks and Errors

I am implementing a Networking Layer in Swift. Here is one of the functions. The function works as expected but I want to improve upon it. I am using DispatchQueue to make sure that the callback from the network client is always on the main thread. This ends up repeating the DispatchQueue.main.async in 3 different places.
Also, when I encounter some error when performing the request I still send back nil but as a success.
func getAllStocks(url: String, completion: #escaping (Result<[Stock]?,NetworkError>) -> Void) {
guard let url = URL(string: url) else {
completion(.failure(.invalidURL)) // wrap in DispatchQueue also
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
DispatchQueue.main.async {
completion(.success(nil)) // should I send nil or some sort of failure
}
return
}
let stocks = try? JSONDecoder().decode([Stock].self, from: data)
DispatchQueue.main.async {
completion(.success(stocks))
}
}
}
How can I minimize the code or is it fine?
The goal of the Result type is that you return a non-optional type on success and an error on failure.
I recommend to call completion on the current thread and dispatch the result on the caller side.
And handle also the DecodingError
func getAllStocks(url: String, completion: #escaping (Result<[Stock],Error>) -> Void) {
guard let url = URL(string: url) else {
completion(.failure(NetworkError.invalidURL))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error { completion(.failure(error)); return }
// if error is nil then data has a value
do {
let stocks = try JSONDecoder().decode([Stock].self, from: data!)
completion(.success(stocks))
} catch {
completion(.failure(error))
}
}.resume()
}
getAllStocks(url: someURL) { result in
DispatchQueue.main.async {
switch result {
case .success(let stocks): print(stocks)
case .failure(let networkError as NetworkError): handleNetworkError(networkError)
case .failure(let decodingError as DecodingError): handleDecodingError(decodingError)
case .failure(let error): print(error)
}
}
}
Lean into the build-in constructs and standard types.
func getAllStocks(url: String, completion: #escaping (Result<[Stock], Error>) -> Void) {
func completeOnMain(_ result: Result<[Stock], Error>) { // <-- Nested function
DispatchQueue.main.async { completion(result) } // <-- Handle repeated work
}
guard let url = URL(string: url) else {
completeOnMain(.failure(URLError(.badURL))) // <-- Standard Error
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
do {
if let error = error { throw error }
guard let data = data else { throw URLError(.badServerResponse) }
let stocks = try JSONDecoder().decode([Stock].self, from: data)
completeOnMain(.success(stocks))
} catch {
completeOnMain(.failure(error)) // <-- Unified error handling
}
}
}
A nested function is used to do the repeated work of dispatching to the main thread.
Standard error are used instead of defining custom errors.
A do/catch and throws are used to handle all the errors at once.
I have one final note: Async functions should always be async. The bad URL error should not call completion(_:) directly; use DispatchQueue.main.async to make sure the call happens in a later run loop.

Call can throw, but it is not marked with 'try' and the error is not handled Swift 4, SwiftyJSON [duplicate]

I am trying to use swiftyjson and I am getting an Error:
Call can throw, but it is marked with 'try' and the error is not
handled.
I have validated that my source JSON is good. I've been searching and cannot find a solution to this problem
import Foundation
class lenderDetails
{
func loadLender()
{
let lenders = ""
let url = URL(string: lenders)!
let session = URLSession.shared.dataTask(with: url)
{
(data, response, error) in
guard let data = data else
{
print ("data was nil?")
return
}
let json = JSON(data: data)
print(json)
}
session.resume()
}
}
Thank you for all the help!
The SwiftyJSON initializer throws, the declaration is
public init(data: Data, options opt: JSONSerialization.ReadingOptions = []) throws
You have three options:
Use a do - catch block and handle the error (the recommended one).
do {
let json = try JSON(data: data)
print(json)
} catch {
print(error)
// or display a dialog
}
Ignore the error and optional bind the result (useful if the error does not matter).
if let json = try? JSON(data: data) {
print(json)
}
Force unwrap the result
let json = try! JSON(data: data)
print(json)
Use this option only if it's guaranteed that the attempt will never fail (not in this case!). Try! can be used for example in FileManager if a directory is one of the default directories the framework creates anyway.
For more information please read Swift Language Guide - Error Handling
You should wrap it into a do-catch block. In your case:
do {
let session = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let data = data else {
print ("data was nil?")
return
}
let json = JSON(data: data)
print(json)
}
} catch let error as NSError {
// error
}
Probably you need to implement do{} catch{} block. Inside do block you have to call throwable function with try.

API call function with completion handler crashes when accessed from different VC

Can someone fix my function code because I have created a API call function which will get the imageURL for the specific object in my class and display the results in the second view controller. I have created custom completion handler so that the code from second VC is only executed when dowloading of the imageURL is completed.
However, when I am testing this function in the second view controller to print me data that it has arrived I am getting a crash on the print statement line.
Here is the code for my API call function located in Model class file:
func parseImageData(finished: () -> Void) {
let urlPath = _exerciseURL
let url = URL(string: urlPath!)
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("Error while parsing JSON")
}
else {
do {
if let data = data,
let fetchedImageData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
let images = fetchedImageData["results"] as? [[String: Any]] {
for eachImage in images {
let imageUrl = eachImage["image"] as! String
self._imageUrl = URL(string: imageUrl)
}
print(self._imageUrl)
}
}
catch {
print("Error while parsing data.")
}
}
}
task.resume()
finished()
}
And here in the second view controller I am just testing if I can access the code block:
override func viewDidLoad() {
super.viewDidLoad()
exercise.parseImageData() {
print("Arrived Here?") // I am getting crash on this line moving to debug navigator.
}
}
If the crash says something about force unwrapping nil then it's probably because let task = URLSession.shared.dataTask(with: url!) is unwrapping url which is a nil optional variable here.
But your completion handler is called in the wrong place anyway, try putting your finished() callback into the do statement instead. Because finished was executed the moment you called exercise.parseImageData()
if error != nil {
print("Error while parsing JSON")
}
else {
do {
if let data = data,
let fetchedImageData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
let images = fetchedImageData["results"] as? [[String: Any]] {
for eachImage in images {
let imageUrl = eachImage["image"] as! String
self._imageUrl = URL(string: imageUrl)
}
print(self._imageUrl)
finished()
}
}
catch {
print("Error while parsing data.")
}
}

Json Serialisation Swift 3

I am trying to serialise the json in the code below, the logs print out the display names successfully but I get a crash with an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
on the following lines:
print(item["display-name"]! as!String)
Blockquoteself.tableData.append(item["display-name"] as! String)
I can't seem to figure out why, any help much appreciated!
let url = NSURL(string: "https://www.asmserver.co.uk/sally/parsexml.php")!
let task = URLSession.shared.dataTask(with: url as URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
for item in jsonResult {
print(item["display-name"]! as!String)
self.tableData.append(item["display-name"] as! String)
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
You should make sure that you really have a value before you use it and specially before using as!.
Do like this instead:
for item in jsonResult {
guard let name = item["display-name"] as? String else { continue }
print(name)
self.tableData.append(name)
}
If the guard succeeds then you have a value and can use the name variable. You can also add several conditions to the guard statement.
As an alternative to the guard statement, you could also use the similar if let construct:
if let item = item["display-name"] as? String {
print(item)
} else {
print("No display name")
}

Try block not working correctly

I'm working on a app in which the api is called through NSURLSession. When the Api works correctly there is no problem but when no data is received due to any error then after Serialization it throws error but the else block for it is never called
let task = session.dataTaskWithRequest(request) { (let data, let response, let error) in
do {
guard let data:NSData = data , let response: NSURLResponse = response where error == nil else {
throw error!
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else{
print("Serialization failed") //This block never executes even if the Serialization Fails
throw JSONError.ConversionFailed
}
guard json.valueForKey("success") != nil else {
return
}
self.apidata = json
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
print(json.valueForKey("success")!)
}
catch let error as JSONError{
self.showalertview(error.rawValue)
print(error.rawValue)
} catch let error as NSError{
print(error.debugDescription)
}
}
task.resume()
What I'm doing wrong here???
Consider:
do {
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
}
} catch {
// B
}
If NSJSONSerialization throws an error (i.e. if it wasn't really a JSON response or if the response was malformed), it will proceed directly to B and the guard statement doesn't come into play. The guard statement will only execute A if and only if (a) the NSJSONSerialization call, itself, didn't throw any errors (i.e. the JSON was well-formed); but (b) the cast to the the dictionary failed (e.g. the top level JSON object was an array instead of a dictionary). That's an extremely unlikely scenario (your server would have to accidentally return a well formed JSON response that was not a dictionary, e.g. a JSON array).
To accomplish what you want, you would use try? to make sure that NSJSONSerialization wouldn't throw any errors, itself:
do {
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
// A
throw JSONError.ConversionFailed
}
} catch {
// B
}
By doing this, only if A performs a throw will B be called

Resources