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.
Related
Basicly I got this,
// MyPackage.swift
enum Error: LocalizedError {
case general(String)
}
func foobar() throws {
throw Error.general("haha")
}
do {
try foobar()
} catch Error.general(let message) {
// will print "haha"
print(message)
}
And then in the unit test, I need to check if I got the exact same error,
import Quick
import Nimble
import MyPackage
class MySpec: QuickSpec {
override func spec() {
describe("") {
let input = "haha"
let count = 2
let expectation = Error.general("非纯数字字符串无法使用本方法")
context("输入_" + input) {
it("预期_" + expectation.localizedDescription) {
// got the error
// but expectation.localizedDescription was not what I looking for
expect(try numberStringByAddingZerosInto(input, tillReach: count))
.to(throwError(expectation))
}
}
}
}
}
It worked, but expectation.localizedDescription was not "haha", which made the name of the test case useless.
I also tried expectation.errorDescription with no luck.
Where could I get it?
And why is it like this?
override errorDescription var
enum Error: LocalizedError {
case general(String)
var errorDescription: String? {
switch self {
case .general(let errorMessage):
return errorMessage
}
}
}
Now, you can also write do-catch block by this
do {
try foobar()
} catch let error {
print(error.localizedDescription)
}
I want to add a value to Firestore. When finished I want to return the added value. The value does get added to Firestore successfully. However, the value does not go through sink.
This is the function that does not work:
func createPremium(user id: String, isPremium: Bool) -> AnyPublisher<Bool,Never> {
let dic = ["premium":isPremium]
return Future<Bool,Never> { promise in
self.db.collection(self.dbName).document(id).setData(dic, merge: true) { error in
if let error = error {
print(error.localizedDescription)
} else {
/// does get called
promise(.success(isPremium))
}
}
}.eraseToAnyPublisher()
}
I made a test function that works:
func test() -> AnyPublisher<Bool,Never> {
return Future<Bool,Never> { promise in
promise(.success(true))
}.eraseToAnyPublisher()
}
premiumRepository.createPremium(user: userID ?? "1234", isPremium: true)
.sink { receivedValue in
/// does not get called
print(receivedValue)
}.cancel()
test()
.sink { recievedValue in
/// does get called
print("Test", recievedValue)
}.cancel()
Also I have a similar code snippet that works:
func loadExercises(category: Category) -> AnyPublisher<[Exercise], Error> {
let document = store.collection(category.rawValue)
return Future<[Exercise], Error> { promise in
document.getDocuments { documents, error in
if let error = error {
promise(.failure(error))
} else if let documents = documents {
var exercises = [Exercise]()
for document in documents.documents {
do {
let decoded = try FirestoreDecoder().decode(Exercise.self, from: document.data())
exercises.append(decoded)
} catch let error {
promise(.failure(error))
}
}
promise(.success(exercises))
}
}
}.eraseToAnyPublisher()
}
I tried to add a buffer but it did not lead to success.
Try to change/remove .cancel() method on your subscriptions. Seems you subscribe to the publisher, and then immediately cancel the subscription. The better option is to retain and store all your subscriptions in the cancellable set.
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
}
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")
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.