Using Do/Catch in Swift - swift

I am working on an app and want to get data back from a function. However sometimes data is missing or is different from the kind of that I want to retrieve. I am new to Swift and I can't find a way to write a function that does a little bit of processing and returns this data. When this data is missing, the function should give back a string "Not Found". Like this:
func processData(data:String) {
do {
//processing
var result = processedData
} catch {
var result = "Not Found"
}
return result
}
It would be very nice if somebody could help me.

You should check if the result is nil.
func processData(data: String?) -> String {
guard let result = data else {
return "Not Found"
  }
return result
}

The most concise way of doing it would be using the guard-let construct:
func processData(data: String?) -> String {
// Assuming getProcessedData(:) returns your processed data
guard let result = getProcessedData(data) else {
return "Not found"
}
return result
}
Also, your function is missing a return type. You must specify the return type like -> TYPE in all functions that return some value.

Those answer were written till mine are right. There is one way: with handler check get result and use by your point.
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?, completionHandler: #escaping (_ result: String? , _ error: Error?) -> Void ) {
guard let data = data else {
// Data is missing
throw nil, Errors.noData
}
// Do other things, and throw if necessary
result = data
return result, nil
}
// example of calling this function
process(data: "A data to process"){(result, error) -> Void in
//do any stuff
/*if error == nil {
}*/
}

A good practice in swift would be to use correctly the throws errors
This is an example inspired from yours :
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?) throws -> String {
guard let data = data else {
// Data is missing
throw Errors.noData
}
// Do other things, and throw if necessary
result = data
return result
}
do {
try process(data: "A data to process")
} catch {
print("An error occurred: \(error)")
}
You can try this code as is in a Swift Playgound

Your function needs to be explicit about returning something with e.g. -> String Also do-catch is for methods that can throw an error. It seems like you need to take a look at how to use optionals. Optionals can have a value or they can have no value.
fun processData(data: String) -> String {
var result: String?
// Do some processing and assign the result to result variable
guard let result = result else { return "Not Found" }
return result
}

Related

Missing return in global function expected to return 'String' message in a function

I know this error is a common message and has already been the subject of many posts. However, as a pure beginner who just started days ago, I can't really understand the solution on other posts, and also haven't learned what Switch means. Thefore, that solution can't be used with me. Here's my block code getting the error :
func responseTo(question: String) -> String {
let lowercasedQuestion = question.lowercased()
if lowercasedQuestion.hasPrefix("hello") {
if lowercasedQuestion.hasPrefix("Hello") {
return "Why, hello there!"
} else if lowercasedQuestion.hasPrefix("where") {
if lowercasedQuestion.hasPrefix("Where") {
return "To the North"
} else {
return "Where are the cookies?"
}
}
}
}
I tried to put the last else outside the first if since I read it could change the output and remove the error, but it didn't change anything. I tried to enter return nil on the last line, but had an error. What can I do? Any answer appreciated.
Your responseTo(String) -> String function must return a String. You must consider this case, if the parameter (question) doesn't start with "hello", the function doesn't have any String to return.
let result: String = responseTo("asd") // error
As stated in the comments, there are several ways to solve this.
If your function must return a string, then consider returning a default value at the end. The return value can be an empty string (but whatever your default value is, make sure to handle it properly).
func responseTo(question: String) -> String {
let lowercasedQuestion = question.lowercased()
if lowercasedQuestion.hasPrefix("hello") {
//
} else {
return "" // this will be the default return value
}
}
or
func responseTo(question: String) -> String {
let lowercasedQuestion = question.lowercased()
if lowercasedQuestion.hasPrefix("hello") {
//
}
return "" // this will also be the default return value
}
Another way is to return an Optional String (String?). The reason why return nil doesn't work for responseTo(String) -> String is because it must return a string. To be able to return a nil, you will have to change the declaration of your function to responseTo(String) -> String?.
func responseTo(question: String) -> String? { // the "?" after String notifies the compiler that this function can return a String or a nil
let lowercasedQuestion = question.lowercased()
if lowercasedQuestion.hasPrefix("hello") {
//
}
return nil
}
you can read more about function here and optional here

How to fix "Invalid conversion from throwing function of type X to non-throwing function type Y" with Do-Try-Catch

I am getting the error: Invalid conversion from throwing function of type '(_) throws -> ()' to non-throwing function type '(Either) -> Void'
on the line after do {, weatherApi.weather(with: weatherEndpoint) { (either) in
class WeatherTableViewController: UITableViewController {
var cellViewModels = [WeatherCellViewModel]()
override func viewDidLoad() {
super.viewDidLoad()
let weatherApi = WeatherAPIClient()
let weatherEndpoint = WeatherEndpoint.fiveDayForecast(city: "Atlanta", country: "us")
do {
weatherApi.weather(with: weatherEndpoint) { (either) in
switch either {
case .value(let Weather):
print("Made it")
print(Weather)
let data = try Weather.map {
$0.weather.map {
WeatherCellViewModel(description: $0.description)
}
}
print("data")
print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .error(let error):
print(error)
}
}
} catch {
print("weather endpoint error")
}
}
I added the do try catch after getting the same error originally and looking at the answers to similar problems, but it did not solve it. The try block is the critical part causing the error, where I am parsing my data for the description.
The do-catch is currently outside of the closure that has the try statement in it.
You'll need to move the do-catch into the closure. The error is because weather is expecting a closure that doesn't throw but it's getting on that does.
You wanted to throw from within the closure, which is not possible. so you need to place do-catch into the closure
Try this way:
do {
let data = try Weather.map {
$0.weather.map {
WeatherCellViewModel(description: $0.description)
}
}
} catch let error as NSError {
print(error)
}

How to write a template function that handle do catch

I want to write a template function that handle do catch. It may look like this
func handleMethod(methodNeedToHandle) -> result: notSureType{
var result
do {
let response = try handleMethod
result = response
return result
} catch let error as someObjectError{
result = error
return result
}
}
Then you can use it like
let catchResult = handleMethod(method(x: something, y: something))
Thank you guys help me a lot, I get working code below
func handleDoTryCatch<T>(closure:() throws -> T) -> Any {
do {
let result = try closure()
return result
} catch let error {
return error
}
}
You could use a generic function that takes a closure and returns a tuple.
Something like:
func handle<T>(closure:() throws -> T) -> (T?, Error?) {
do {
let result = try closure()
return (result, nil)
} catch let error {
return (nil, error)
}
}
This will define a function that takes a closure that calls the method that can throw. It the returns a tuple with the expected return type and something that conforms to the Error protocol.
You would use it like this:
let result: (Void?, Error?) = handle { try someFunc() }
let result2: (Int?, Error?) = handle { try someOtherFunc(2) }
someFunc and someOtherFunc are just examples and their signatures would be:
func someFunc() throws {}
func someOtherFunc(_ param: Int) throws -> Int {}
This is the function I managed to come up with:
// Your error cases
enum Errors: Error {
case someErrorCase
}
// Function to test another function
func doTryCatch<T>(for function: () throws -> T) {
do {
let returnValue = try function()
print("Success! The return value is \(returnValue)")
} catch {
print("Error! The error reason was \"\(String(describing: error))\"")
}
}
// Function to test
func failingFunction() throws -> Int {
throw Errors.someErrorCase // <-- Comment this to not recieve an error (for testing)
return 5 // Will return 5 if the error is not thrown
// Failure: Error! The error reason was "someErrorCase"
// Success: Success! The return value is 5
}
// Perform the test
doTryCatch(for: failingFunction) // <-- Very easy to test, no closures to write!
Hope this helps with your debugging! :)
The closest I could get to what you probably want is the following:
(Swift Playground code:)
func handleMethod(_ f: #autoclosure () throws -> Void) -> Error? {
do {
try f()
} catch let err {
return err
}
return nil
}
enum HelloError: Error {
case tooShort
}
func hello(_ what: String) throws {
guard what.count > 0 else { throw HelloError.tooShort }
print ("Hello \(what)!")
}
// use like this:
// let err = handleMethod(try hello("World")) // err == nil
// let err = handleMethod(try hello("")) // err == HelloError.tooShort
//
print ("* hello(\"World\") -> \(String(describing: handleMethod(try hello("World"))))")
print ("* hello(\"\") -> \(String(describing: handleMethod(try hello(""))))")
This will produce the following output:
Hello World!
* hello("World") -> nil
* hello("") -> Optional(__lldb_expr_3.HelloError.tooShort)
Consider just using do/catch as George_E recommends. It's a good advice. But if you need this function, than this snipped hopefully gives you a starting point.

Is it possible to abort a map function on a Swift collection?

We have a case where we're being handed an object of type Array<Any> which we need to convert to an Array<Codable>. If any of the items in the original array don't adhere to Codable, then we want the entire thing to abort and return nil.
Or current approach is to manually loop over everything, testing along the way, like so...
func makeCodable(sourceArray:Array<Any>) -> Array<Codable>?{
var codableArray = Array<Codable>()
for item in sourceArray{
guard let codableItem = item as? Codable else {
return nil
}
codableArray.append(codableItem)
}
return codableArray
}
However, I'm wondering if there's an easier way to do this with the map command, but it would require it to short-circuit if any of the elements can't be mapped. That's what I'm not sure or not is possible.
For instance, this pseudo-code...
func makeCodable(sourceArray:Array<Any>) -> Array<Codable>?{
return sourceArray.map({ $0 as? Codable});
}
Is this possible, or is our original way the correct/only way?
Here's one solution using map and throws.
func makeCodable(sourceArray: [Any]) -> [Codable]? {
enum CodableError: Error {
case notCodable
}
let res: [Codable]? = try? sourceArray.map {
guard let codable = $0 as? Codable else {
throw CodableError.notCodable
}
return codable
}
return res
}
let res = makeCodable2(sourceArray: [5, 6.5, "Hi", UIView()])
print(res) // nil
Here's a variation that makes makeCodable throw and return a non-optional array:
enum CodableError: Error {
case notCodable
}
func makeCodable(sourceArray: [Any]) throws -> [Codable] {
let res: [Codable] = try sourceArray.map {
guard let cod = $0 as? Codable else {
throw CodableError.notCodable
}
return cod
}
return res
}
do {
let res = try makeCodable(sourceArray: [5, 6.5, "Hi"])
print(res) // prints array
let bad = try makeCodable(sourceArray: [5, 6.5, "Hi", UIView()])
print(bad)
} catch {
print(error) // goes here on 2nd call
}
As #rmaddy shows, you can utilise the fact that map(_:) can accept a throwing closure, and will stop mapping upon an error being thrown, propagating the error thrown back to the caller (which you can then absorb with try?).
One slight variation on this would be to define your own throwing cast(_:to:) function to call in the transformation closure:
struct TypeMismatchError : Error {
var expected: Any.Type
var actual: Any.Type
}
func cast<T, U>(_ x: T, to _: U.Type) throws -> U {
guard let casted = x as? U else {
throw TypeMismatchError(expected: U.self, actual: type(of: x))
}
return casted
}
func makeCodable(sourceArray: [Any]) -> [Codable]? {
return try? sourceArray.map { try cast($0, to: Codable.self) }
}
Although we're completely ignoring the error thrown in this case, I have found it occasionally useful in other cases to have a throwing casting function about (you could of course also propagate the error by making makeCodable a throwing function and using try).
However, that all being said, note that your resulting [Codable]? really isn't too useful in its current form. You can't decode stuff from it because you don't have any concrete types to hand, and you can't directly encode it as protocols don't conform to themselves (i.e Codable doesn't conform to Encodable, so you can't just hand off a Codable or [Codable] to JSONEncoder).
If you actually wanted to do some encoding with your [Codable], you'd need to wrap each of the elements in a Encodable conforming wrapper, for example:
struct AnyEncodable : Encodable {
var base: Encodable
init(_ base: Encodable) {
self.base = base
}
func encode(to encoder: Encoder) throws {
try base.encode(to: encoder)
}
}
func makeEncodable(sourceArray: [Any]) -> [AnyEncodable]? {
return try? sourceArray.map {
AnyEncodable(try cast($0, to: Encodable.self))
}
}
Now [AnyEncodable] is something you can pass off to, for example, JSONEncoder.

Nested do catch swift 3.0

I'd like to use consecutive try statements. If one returns an error I'd like to proceed to the next one, otherwise return the value.
The code below seems to work fine, however I'll end up with a big nested do catch pyramid. Is there a smarter/better way to do it in Swift 3.0?
do {
return try firstThing()
} catch {
do {
return try secondThing()
} catch {
return try thirdThing()
}
}
If the actual errors thrown from those function calls are not needed
then you can use try? to convert the result to an optional,
and chain the calls with the nil-coalescing operator ??.
For example:
if let result = (try? firstThing()) ?? (try? secondThing()) ?? (try? thirdThing()) {
return result
} else {
// everything failed ...
}
Or, if the error from the last method should be thrown if everything fails,
use try? for all but the last method call:
return (try? firstThing()) ?? (try? secondThing()) ?? (try thirdThing())
If Martin's answer is too terse for your taste you can just go with individual catch blocks.
do {
return try firstThing()
} catch {}
do {
return try secondThing()
} catch {}
do {
return try thirdThing()
} catch {}
return defaultThing()
As each throwing function's result is immediately returned no nesting is necessary.
Another way to do this is to write a function that takes all your throwing functions as an argument. It returns the first one that was successfully executed or nil.
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
The lazy ensures that the values are only called until it finds the first match. Doing it this way, you can also add a lot of cases very quickly.
You can use the function like this
return first(firstThing, secondThing, thirdThing) ?? "Default"
I also included the code I used to test this in playground:
enum ThingError: Error {
case zero
}
func firstThing() throws -> String {
print("0")
throw ThingError.zero
return "0"
}
func secondThing() throws -> String {
print("1")
return "1"
}
func thirdThing() throws -> String {
print("2")
return "B"
}
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
func tryThings() -> String {
return first(firstThing, secondThing, thirdThing) ?? "Default"
}
tryThings() // prints "0" and "1"