I keep wanting to do this:
do {
let result = try getAThing()
} catch {
//error
}
do {
let anotherResult = try getAnotherThing(result) //Error - result out of scope
} catch {
//error
}
But seem only to be able to do this:
do {
let result = try getAThing()
do {
let anotherResult = try getAnotherThing(result)
} catch {
//error
}
} catch {
//error
}
Is there a way to keep an immutable result in scope without having to nest do/catch blocks? Is there a way to guard against the error similar to how we use the guard statement as an inverse of if/else blocks?
In Swift 1.2, you can separate the declaration of the constant from the assignment of the constant. (See "Constants are now more powerful and consistent" in the Swift 1.2 Blog Entry.) So, combining that with the Swift 2 error handling, you can do:
let result: ThingType
do {
result = try getAThing()
} catch {
// error handling, e.g. return or throw
}
do {
let anotherResult = try getAnotherThing(result)
} catch {
// different error handling
}
Alternatively, sometimes we don't really need two different do-catch statements and a single catch will handle both potential thrown errors in one block:
do {
let result = try getAThing()
let anotherResult = try getAnotherThing(result)
} catch {
// common error handling here
}
It just depends on what type of handling you need.
Related
Not sure why i'm getting this.. any suggestions would be grateful!
I ran into issues with my original coding where I had Firebase pod and Firebase Package.. so I started from scratch since that wasnt fixing itself.. now I get this.. and I am at a loss for how to resolve it.
static func fetchUsers() -> AnyPublisher<[UserProfile], Error> {
Future< [UserProfile], Error > { promise in
self.db.collection("Users")
.getDocuments { (snapshot, error) in
if let error = error {
promise(.failure(error))
return
}
guard let snapshot = snapshot else {
promise(.failure(FirebaseError.badSnapshot))
return
}
var users = [UserProfile]()
snapshot.documents.forEach { document in
print(users.count)
if let user = try? document.data(as: UserProfile.self){
if users.contains(where: { $0.id == user.id}) {return}
users.append(user)
} else {
print("Not working")
}
}
promise(.success(users))
}
}
.eraseToAnyPublisher()
}
I believe this is the syntax you're after:
var users = [UserProfile]()
users = snapshot.documents.compactMap { (document) -> UserProfile? in
if users.contains(where: { $0.id == user.id}) {
return nil
} else {
return try? document.data(as: UserProfile.self)
}
}
Also be aware that when you iterate something in Swift and encounter a false condition on an iteration, return will return out of the greater scope, not just that iteration. Therefore, use continue.
for x in y {
guard x > 0 else {
continue // continues loop
}
// ...
}
I have the following code for a Jasmine Custom Matcher, as described here:
jasmine.addMatchers({
testingFunction: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
if (expected === undefined) {
expected = '';
}
var result = {};
result.pass = util.equals(actual.myValue, 1, customEqualityTesters);
if (result.pass) {
result.message = "Passed";
} else {
result.message = "Failed";
}
return result;
}
}
}
});
And calling it as such:
.then(function() {
expect({
myValue: 1
}).testingFunction();
})
During debugging, I see that execution goes to my custom matcher but for some reason, neither my Pass or Fail messages get printed to the console.
Any ideas as to why this might be?
Thanks
For anyone else that may be running into this issue, I figured out that in my jasmineNodeOpts, I was overriding the jasmine print method as such:
// Overrides jasmine's print method to report dot syntax for custom reports
//print: () => {},
Removing that fixed my issue.
after upgrading to new version of Xcode/swift i am getting error "Ambiguous use of subscript before was work well but now i am getting this error
my code for search in my data
#IBAction func searchB(sender: UITextField) {
dataSecond = []
if sender.text == "" {
search = false
self.reload()
} else {
for i in data {
if (i["name"] as! String!).lowercaseString.findInString(sender.text!) { //Here is the error in this if condition
dataSecond.addObject(i)
}
}
search = true
self.reload()
}
Swift 2.2 is rather strict about types, much more strict than the previous versions. What was implicit before has now to be explicit.
Use safe unwrapping and downcasting and it should work:
if let senderText = sender.text {
for i in data {
if let name = i["name"] as? String {
if name.lowercaseString.findInString(senderText) {
dataSecond.addObject(i)
}
}
}
}
How to test wether a function in Swift 2.0 throws or not? How to assert that the correct ErrorType is thrown?
EDIT: Updated the code for Swift 4.1 (still valid with Swift 5.2)
Here's the latest Swift version of Fyodor Volchyok's answer who used XCTAssertThrowsError:
enum MyError: Error {
case someExpectedError
case someUnexpectedError
}
func functionThatThrows() throws {
throw MyError.someExpectedError
}
func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
XCTAssertEqual(error as! MyError, MyError.someExpectedError)
}
}
If your Error enum has associated values, you can either have your Error enum conform to Equatable, or use the if case statement:
enum MyError: Error, Equatable {
case someExpectedError
case someUnexpectedError
case associatedValueError(value: Int)
}
func functionThatThrows() throws {
throw MyError.associatedValueError(value: 10)
}
// Equatable pattern: simplest solution if you have a simple associated value that can be tested inside 1 XCTAssertEqual
func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
XCTAssertEqual(error as! MyError, MyError.associatedValueError(value: 10))
}
}
// if case pattern: useful if you have one or more associated values more or less complex (struct, classes...)
func testFunctionThatThrows() {
XCTAssertThrowsError(try functionThatThrows()) { error in
guard case MyError.associatedValueError(let value) = error else {
return XCTFail()
}
XCTAssertEqual(value, 10)
// if you have several values or if they require more complex tests, you can do it here
}
}
At least of Xcode 7.3 (maybe earlier) you could use built-in XCTAssertThrowsError():
XCTAssertThrowsError(try methodThatThrows())
If nothing is thrown during test you'll see something like this:
If you want to check if thrown error is of some concrete type, you could use errorHandler parameter of XCTAssertThrowsError():
enum Error: ErrorType {
case SomeExpectedError
case SomeUnexpectedError
}
func functionThatThrows() throws {
throw Error.SomeExpectedError
}
XCTAssertThrowsError(try functionThatThrows(), "some message") { (error) in
XCTAssertEqual(error as? Error, Error.SomeExpectedError)
}
Given the following functions and declarations:
enum SomeError: ErrorType {
case FifthError
case FirstError
}
func throwingFunction(x: Int) throws {
switch x {
case 1:
throw SomeError.FirstError
case 5:
throw SomeError.FifthError
default:
return
}
}
This function will throw a FifthError if 5 is given to the function and FirstError if 1 is given.
To test, that a function successfully runs the unit test could look as follows:
func testNotError() {
guard let _ = try? throwingFunction(2) else {
XCTFail("Error thrown")
return
}
}
The let _ may also be replaced by any other name, so you can further test the output.
To assert that a function throws, no matter what ErrorType the unit test could look like this:
func testError() {
if let _ = try? throwingFunction(5) {
XCTFail("No error thrown")
return
}
}
If you want to test for a specific ErrorType it's done with a do-catch-statement. This is not the best way compared to other languages.
You have to make sure that you...
return in the catch for the correct ErrorType
XCTFail() and return for all other catch
XCTFail() if no catch is executed
Given this requirements a test case could look like this:
func testFifthError() {
do {
try throwingFunction(5)
} catch SomeError.FifthError {
return
} catch {
XCTFail("Wrong error thrown")
return
}
XCTFail("No error thrown")
}
Swift 4.1 Error throwing Test for associated values
enum ParseError: Error, Equatable {
case unexpectedArgument(String)
}
func testWithNoSchemaButWithOneArgument() {
XCTAssertThrowsError(try Args(withSchema: "", andArguments: ["-x"])) { error in
XCTAssertEqual(error as? ParseError, ParseError.unexpectedArgument("Argument(s) -x unexpected."))
}
}
You can use this function:
func XCTAssertThrowsError<T, E: Error & Equatable>(
_ expression: #autoclosure () throws -> T,
error: E,
in file: StaticString = #file,
line: UInt = #line
) {
var thrownError: Error?
XCTAssertThrowsError(
try expression(),
file: file,
line: line) {
thrownError = $0
}
XCTAssertTrue(
thrownError is E,
"Unexpected error type: \(type(of: thrownError))",
file: file,
line: line
)
XCTAssertEqual(
thrownError as? E,
error,
file: file,
line: line
)
}
Example:
XCTAssertThrowsError(try funcThatThrowsSpecificError(), error: SpecificErrorEnum.someError)
HI i am a little confused about how to transfer if_else error handling to do try catch successfully.
Here is my code.
let error : NSError?
if(managedObjectContext!.save()) {
NSNotificationCenter.defaultCenter().postNotificationName("updateUndoState", object: nil)
if error != nil {
print(error?.localizedDescription)
}
}
else {
print("abort")
abort()
}
and now i converted to swift 2.0 like this
do {
try managedObjectContext!.save()
}
catch {
NSNotificationCenter.defaultCenter().postNotificationName("updateUndoState", object: nil)
print((error as NSError).localizedDescription)
}
I am confused about where to print abort and do the abort() function
Any idea~? Thanks a lot
Rewriting your code to work the same as your original code
do {
try managedObjectContext!.save()
//this happens when save did pass
NSNotificationCenter.defaultCenter().postNotificationName("updateUndoState", object: nil)
//this error variable has nothing to do with save in your original code
if error != nil {
print(error?.localizedDescription)
}
}
catch {
//this happens when save() doesn't pass
abort()
}
what you probably want to write is the following:
do {
try managedObjectContext!.save()
//this happens when save did pass
NSNotificationCenter.defaultCenter().postNotificationName("updateUndoState", object: nil)
}
catch let saveError as NSError {
//this happens when save() doesn't pass
print(saveError.localizedDescription)
abort()
}
Everything within do {} is good, everything within catch {} is bad
do {
try managedObjectContext!.save()
NSNotificationCenter.defaultCenter().postNotificationName("updateUndoState", object: nil)
}
catch let error as NSError {
print(error.localizedDescription)
abort()
}
use either the error handling or the abort() statement