Swift 2 ( Executefetchrequest ): Error Handling

Swift 2 ( executeFetchRequest ) : error handling

As of Swift 2, Cocoa methods that produce errors are translated to Swift functions that throw an error.

Instead of an optional return value and an error parameter as in Swift 1.x:

var error : NSError?
if let result = context.executeFetchRequest(request, error: &error) {
// success ...
list = result
} else {
// failure
println("Fetch failed: \(error!.localizedDescription)")
}

in Swift 2 the method now returns a non-optional and throws an error
in the error case, which must be handled with try-catch:

do {
list = try context.executeFetchRequest(request)
// success ...
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}

For more information, see "Error Handling" in "Adopting Cocoa Design Patterns"
in the "Using Swift with Cocoa and Objective-C" documentation.

Core data error handling

In Swift 2.0 you have to use exception handling:

var fetchedResults : [NSManagedObject]?

do {
fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
} catch let error as NSError {
print("Error \(error.localizedDescription)")
}

Apple documentation

executeFetchRequest:error: A fetch request must have an entity

It seemed as if my data got corrupted, so I deleted my data model and the database in the iPhone simulator, and started fresh.

Fetch Requests, Core Data and Error Handling in Swift 2

Local variables are visible only within the scope they are declared in, in the case of fetchResult the do block.

Put all the code to be executed if the fetch succeeds also in the do block and any optional binding is not needed at all because peopleArray is valid.

I simplified the code a bit.

   do {
let peopleArray = try managedObjectContext.executeFetchRequest(request) as! [Peoples]

for person in peopleArray
{
strPhoneNum = person.valueForKey("phone") as! String
strFullName = person.valueForKey("name") as! String
phoneArray.addObject(strPhoneNum)//Add phone number in array
nameArray.addObject(strFullName)//Add name in arrray
}
contactTable.reloadData()

} catch var error as NSError{
print("Fetch Failed")
}

Error-Handling in Swift-Language

Swift 2 & 3

Things have changed a bit in Swift 2, as there is a new error-handling mechanism, that is somewhat more similar to exceptions but different in detail.

1. Indicating error possibility

If function/method wants to indicate that it may throw an error, it should contain throws keyword like this

func summonDefaultDragon() throws -> Dragon

Note: there is no specification for type of error the function actually can throw. This declaration simply states that the function can throw an instance of any type implementing ErrorType or is not throwing at all.

2. Invoking function that may throw errors

In order to invoke function you need to use try keyword, like this

try summonDefaultDragon()

this line should normally be present do-catch block like this

do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}

Note: catch clause use all the powerful features of Swift pattern matching so you are very flexible here.

You may decided to propagate the error, if your are calling a throwing function from a function that is itself marked with throws keyword:

func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}

Alternatively, you can call throwing function using try?:

let dragonOrNil = try? summonDefaultDragon()

This way you either get the return value or nil, if any error occurred. Using this way you do not get the error object.

Which means that you can also combine try? with useful statements like:

if let dragon = try? summonDefaultDragon()

or

guard let dragon = try? summonDefaultDragon() else { ... }

Finally, you can decide that you know that error will not actually occur (e.g. because you have already checked are prerequisites) and use try! keyword:

let dragon = try! summonDefaultDragon()

If the function actually throws an error, then you'll get a runtime error in your application and the application will terminate.

3. Throwing an error

In order to throw an error you use throw keyword like this

throw DragonError.dragonIsMissing

You can throw anything that conforms to ErrorType protocol. For starters NSError conforms to this protocol but you probably would like to go with enum-based ErrorType which enables you to group multiple related errors, potentially with additional pieces of data, like this

enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}

Main differences between new Swift 2 & 3 error mechanism and Java/C#/C++ style exceptions are follows:

  • Syntax is a bit different: do-catch + try + defer vs traditional try-catch-finally syntax.
  • Exception handling usually incurs much higher execution time in exception path than in success path. This is not the case with Swift 2.0 errors, where success path and error path cost roughly the same.
  • All error throwing code must be declared, while exceptions might have been thrown from anywhere. All errors are "checked exceptions" in Java nomenclature. However, in contrast to Java, you do not specify potentially thrown errors.
  • Swift exceptions are not compatible with ObjC exceptions. Your do-catch block will not catch any NSException, and vice versa, for that you must use ObjC.
  • Swift exceptions are compatible with Cocoa NSError method conventions of returning either false (for Bool returning functions) or nil (for AnyObject returning functions) and passing NSErrorPointer with error details.

As an extra syntatic-sugar to ease error handling, there are two more concepts

  • deferred actions (using defer keyword) which let you achieve the same effect as finally blocks in Java/C#/etc
  • guard statement (using guard keyword) which let you write little less if/else code than in normal error checking/signaling code.

Swift 1

Runtime errors:

As Leandros suggests for handling runtime errors (like network connectivity problems, parsing data, opening file, etc) you should use NSError like you did in ObjC, because the Foundation, AppKit, UIKit, etc report their errors in this way. So it's more framework thing than language thing.

Another frequent pattern that is being used are separator success/failure blocks like in AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})

Still the failure block frequently received NSError instance, describing the error.

Programmer errors:

For programmer errors (like out of bounds access of array element, invalid arguments passed to a function call, etc) you used exceptions in ObjC. Swift language does not seem to have any language support for exceptions (like throw, catch, etc keyword). However, as documentation suggests it is running on the same runtime as ObjC, and therefore you are still able to throw NSExceptions like this:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

You just cannot catch them in pure Swift, although you may opt for catching exceptions in ObjC code.

The questions is whether you should throw exceptions for programmer errors, or rather use assertions as Apple suggests in the language guide.

Errors thrown from here are not handled for do { } catch in Swift 2.0

The error is telling you that the enclosing catch is not exhaustive. This is because the auto-generated catch block is only catching NSError objects, and the compiler can't tell whether some other ErrorType will be thrown.

If you're sure no other errors will be thrown, you can add another default catch block:

do {
objects = try managedObjectContext?.executeFetchRequest(request)
} catch let error1 as NSError {
error = error1
objects = nil
} catch {
// Catch any other errors
}

Swift: CoreData error and how to set up entities and DataModels?

You cannot use custom (simple) classes as Core Data model.

Classes used as Core Data model must be a subclass of NSManagedObject.

If your entity is named UserData you have to declare the array

var users = [UserData]()

The User class is useless.

Casting from 'NSPersistentStoreResult' to unrelated type 'Entity' always fails

Use executeFetchRequest method of NSManagedObjectContext when performing a NSFetchRequest.

Edit Swift 3: For Swift 3, use:

let result = try managedContext.fetch(fetchRequest)


Related Topics



Leave a reply



Submit