I'd like to generate an NSAlert-style error sheet from a Swift exception handler. In the past with Objective-C I could simply use [NSAlert alertWithError:] Now with Swift and it's own exception handling system in the mix, alerts feel rather clunky:
enum Problems: ErrorType {
case Bad
case Worse
}
func f() throws {
throw Problems.Worse
}
func g() {
let errorMessage: String
do {
try f()
}
catch Problems.Bad {
errorMessage = "This was bad"
}
catch Problems.Worse {
errorMessage = "This was even worse"
}
catch {
errorMessage = "This was unexpected"
}
guard errorMessage == nil else {
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = errorMessage
alert.beginSheetModalForWindow(self, completionHandler: { [unowned self] (returnCode) -> Void in
if returnCode == NSAlertFirstButtonReturn {
// Handle the response
}
})
}
This feels kludgy and like it doesn't scale well if there are many error conditions that must be checked. Is there a better way?
Swift allows your ErrorType to have associated data. This is more or less an exploded version of userInfo from NSError. If you want convenience, you can write a function to convert a Problems enum to NSError:
enum Problems: ErrorType {
case Small
case Bad(message: String?)
case Worse(message: String?)
func toNSError() -> NSError {
var errorMessage = ""
switch self {
case .Small:
errorMessage = "Small error"
case let .Bad(msg):
errorMessage = msg ?? "Default bad message"
case let .Worse(msg):
errorMessage = msg ?? "Default worse message"
}
return NSError(domain: NSCocoaErrorDomain, code: 1, userInfo: [NSLocalizedDescriptionKey: errorMessage])
}
}
func f() throws {
throw Problems.Worse(message: nil)
}
do {
try f()
} catch let error as Problems {
let err = error.toNSError()
// now display NSAlert with err...
}
You can throw an error with message: nil to get the default message, or add your own custom message from the local conditions:
throw Problems.Bad(message: "I have a bad feeling about this")
Related
I want to add code to throw an error that requires the "catch let printerErrorsecond" and the last catch block to handle an error. I have tried updating the value for the toPrinter parameter to a number of values without any luck. I also tried a number of else if statements and cannot seem to get either of the last two catch blocks to handle the errors. It seems like a case for both could be added in the PrinterError enum but after several attempts I could not resolve the issue. Any code based resolutions you can provide the better!
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
} else if printerName == "Fire" {
throw PrinterError.onFire
} else if printerName == "Empty" {
throw PrinterError.outOfPaper
}
return "Job sent"
}
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
// Prints "Job sent"
try to implement it by this way
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
case deFault
case noHandeling
}
func send(job: Int, toPrinter printerName: String) throws -> String {
switch printerName {
case PrinterError.outOfPaper.localizedDescription:
print("outOfPaper")
return PrinterError.outOfPaper.localizedDescription
case PrinterError.noToner.localizedDescription:
print("noToner")
return PrinterError.noToner.localizedDescription
case PrinterError.onFire.localizedDescription:
print("onFire")
return PrinterError.onFire.localizedDescription
default:
print("default")
return PrinterError.deFault.localizedDescription
}
}
do {
try send(job: 100, toPrinter: "AAA")
} catch {
print(error.localizedDescription)
}
I am trying to learn Swift/Xcode and have come across an error that I can't explain.
Here is some code that should print the error code, but still prints Success even though I have changed websiteUp to false!
enum WebpageError: Error {
case success
case failure(Int)
}
func getWebpage(uRL: String, siteUp: Bool) throws -> String {
if siteUp == false {
WebpageError.failure(404)
}
return "Success"
}
let webpageURL = "http://apple.com"
let websiteUp = false
do {
let status = try getWebpage(uRL: webpageURL, siteUp: websiteUp)
print(status)
} catch {
print(error)
}
I can't spot what is wrong, as it all looks OK to me!
You are doing two silly things:
How can a case of an Error enum be a .success? It's for failures!
It isn't enough to say WebpageError.failure. You have to throw the error.
So:
enum WebpageError: Error {
case failure(Int) // <== look
}
func getWebpage(uRL: String, siteUp: Bool) throws -> String {
if !siteUp {
throw WebpageError.failure(404) // <== look
}
return "Success"
}
let webpageURL = "http://apple.com"
let websiteUp = false
do {
let status = try getWebpage(uRL: webpageURL, siteUp: websiteUp)
print(status)
} catch {
print(error)
}
Having said all that, let's go back to the question of why you included a .success case. Maybe your idea was to return a value signalling success or failure. That would be a Result. Like this:
enum WebpageError: Error {
case failure(Int)
}
func getWebpage(uRL: String, siteUp: Bool) -> Result<String,Error> {
return Result {
if !siteUp {
throw WebpageError.failure(404)
}
return "Success"
}
}
let webpageURL = "http://apple.com"
let websiteUp = false
let status = getWebpage(uRL: webpageURL, siteUp: websiteUp)
do {
try print(status.get())
} catch {
print(error)
}
That might be more sophisticated than you had in mind at this stage, but it's worth thinking about.
I am having problem displaying custom error message on Swift.
The scenario is basic user authentication with Dictionary data, input entered is validated with the Dictionary data, if username and password matches, return true, else an error message as "Invalid Credentials" with AuthenticationError appears (argument message CustomErrors enum)
Here is the code I have come up with so far:
import Foundation
//Editable code starts here
let authDict:Dictionary = ["admin":"admin123","user":"someone123","guest":"guest999","you":"yourpass","me":"mypass190"]
//create CustomErrors enum
public enum CustomErrors: Error {
case AuthenticationError(message:String)
//case invalidCredentials
}
extension CustomErrors: LocalizedError {
public var errorDescription: String? {
switch self{
//case .invalidCredentials:
// return NSLocalizedString("Invalid Credentials", comment: "Invalid Credentials")
//return "Invalid Credentials"
case .AuthenticationError(message:let message):
print(message)
return "Invalid Credentials"
}
}
}
// create authenticate(userName:user,pass:pass) with return type Boolean
func authenticate(user:String,pass:String) -> Bool{
print(user)
print(pass)
for(u,p) in authDict {
if((u == user) && (p == pass)) {
return true
}
}
print("Invalid Credentials")
return false
}
//Editable code ends here
//Uneditable code starts here
let stdout = ProcessInfo.processInfo.environment["OUTPUT_PATH"]!
FileManager.default.createFile(atPath: stdout, contents: nil, attributes: nil)
let fileHandle = FileHandle(forWritingAtPath: stdout)!
guard let user = readLine() else { fatalError("Bad input") }
guard let pass = readLine() else { fatalError("Bad input") }
do{
let result = try authenticate(user:user,pass:pass)
fileHandle.write((result ? "1" : "0").data(using: .utf8)!)
}catch CustomErrors.AuthenticationError(let message){
fileHandle.write(message.data(using: .utf8)!)
}
//Uneditable code ends here
The above code works perfect for all positive test cases like :
1. "admin":"admin123"
2. "user":"someone123"
3. "guest":"guest999"
4. "you":"yourpass"
5. "me":"mypass190"
The output is 1 for all above, but for negative test cases, I should get Invalid Credentials printed as output, instead I get 0
I am not sure what I am missing in the CustomErrors enum but its not working.
How do I fix my editable code to work with the below uneditable code, what should I add/delete to achieve the end result:
do{
let result = try authenticate(user:user,pass:pass)
fileHandle.write((result ? "1" : "0").data(using: .utf8)!)
}catch CustomErrors.AuthenticationError(let message){
fileHandle.write(message.data(using: .utf8)!)
}
Modify the authenticate function as:
func authenticate(user:String,pass:String) throws -> Bool{
print(user)
print(pass)
guard authDict[user] == pass else {
throw CustomErrors.AuthenticationError(message: "Invalid Credentials")
}
return true
}
Now the function throws a CustomErrors error if user and pass do not match those stored in the authDict Dictionary.
I try to become acquainted with the try&catch construct in Swift.
Sorry, i couldn't find an answer in related posts!
Is it possible to hide this into a function or class as shown here?
class Test {
enum TestError:ErrorType{
case a
case b
}
func a(s:String) throws -> String{
if s == "OK" {
return "OK"
}else{
throw TestError.a
}
}
func b(s:String) throws -> String{
if s == "OK" {
return "OK"
}else{
throw TestError.b
}
}
func doTest(str:String) -> String{
do{
let s = try a(b(str)) // <= ERROR
}catch TestError.a{
return "a failed!"
}catch TestError.b{
return "b failed!"
}
}
}
I always get
error: errors thrown from here are not handled because the enclosing
catch is not exhaustive
Is it in principle impossible and only applicable in the main program?
Or is there a way around?
The compiler needs a clause in case a different error is thrown. I like to do a global catch like this, by casting to NSError:
do {
return try a(b(str))
} catch TestError.a {
return "a failed!"
} catch TestError.b {
return "b failed!"
} catch let error as NSError {
return "\(error.description)"
}
You also have to return the String at the try.
I'm using some third-party functions, and I don't know what types of errors they can produce.
I want to catch and log all such errors, but ErrorType protocol does not have any members that could provide a meaningful message or error code. What can be done to log any type of errors properly?
do {
try someFunction()
} catch WellKnownError { // handle it }
} catch let err {
let message = ... // how can I get error type and some meaningful message from err?
NSLog(message)
}
I think it's not an option...
But you can try something like this...
//----------------------------------------
// Private API
//----------------------------------------
public enum CarError : ErrorType
{
case NeverTurnOn
case OutOfFuel
case Other(String)
}
public class Car
{
public func turnOn(newState:Int) throws -> Bool
{
if newState == 1
{
throw CarError.NeverTurnOn
}
else if case 2...5 = newState
{
throw CarError.OutOfFuel
}
else if newState == 10
{
throw CarError.Other("Something is gonna wrong")
}
else if newState == 11
{
throw CarError.Other(":(")
}
return false
}
}
Error Handling
func main()
{
let car = Car()
do {
try car.turnOn(4)
} catch CarError.NeverTurnOn {
print("Turn off")
} catch let err as NSError {
print(err.description)
} catch {
print("It's madness!!!")
}
}
main()
With this test... I had this result...
//Error Domain=trycatchtest.CarError Code=1 "The operation couldn’t be completed. (trycatchtest.CarError error 1.)"
//Program ended with exit code: 0
It's not the best way to solve your problem, but a new way to understand what is happening in background of this framework.
Now, asking a favor.
Please, let me now about the final result ;)
It's doesn't matter if is a good or bad result.