[ios] Swift 2: Call can throw, but it is not marked with 'try' and the error is not handled

After I installed Xcode 7 beta and convert my swift code to Swift 2, I got some issue with the code that I can't figure out. I know Swift 2 is new so I search and figure out since there is nothing about it, I should write a question.

Here is the error:

Call can throw, but it is not marked with 'try' and the error is not handled

Code:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

Snapshot: enter image description here

This question is related to ios xcode swift

The answer is


You have to catch the error just as you're already doing for your save() call and since you're handling multiple errors here, you can try multiple calls sequentially in a single do-catch block, like so:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

Or as @bames53 pointed out in the comments below, it is often better practice not to catch the error where it was thrown. You can mark the method as throws then try to call the method. For example:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

When calling a function that is declared with throws in Swift, you must annotate the function call site with try or try!. For example, given a throwing function:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

this function can be called like:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

Here we annotate the call with try, which calls out to the reader that this function may throw an exception, and any following lines of code might not be executed. We also have to annotate this function with throws, because this function could throw an exception (i.e., when willOnlyThrowIfTrue() throws, then foo will automatically rethrow the exception upwards.

If you want to call a function that is declared as possibly throwing, but which you know will not throw in your case because you're giving it correct input, you can use try!.

func bar() {
  try! willOnlyThrowIfTrue(false)
}

This way, when you guarantee that code won't throw, you don't have to put in extra boilerplate code to disable exception propagation.

try! is enforced at runtime: if you use try! and the function does end up throwing, then your program's execution will be terminated with a runtime error.

Most exception handling code should look like the above: either you simply propagate exceptions upward when they occur, or you set up conditions such that otherwise possible exceptions are ruled out. Any clean up of other resources in your code should occur via object destruction (i.e. deinit()), or sometimes via defered code.

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

If for whatever reason you have clean up code that needs to run but isn't in a deinit() function, you can use defer.

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

Most code that deals with exceptions simply has them propagate upward to callers, doing cleanup on the way via deinit() or defer. This is because most code doesn't know what to do with errors; it knows what went wrong, but it doesn't have enough information about what some higher level code is trying to do in order to know what to do about the error. It doesn't know if presenting a dialog to the user is appropriate, or if it should retry, or if something else is appropriate.

Higher level code, however, should know exactly what to do in the event of any error. So exceptions allow specific errors to bubble up from where they initially occur to the where they can be handled.

Handling exceptions is done via catch statements.

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

You can have multiple catch statements, each catching a different kind of exception.

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

For more details on best practices with exceptions, see http://exceptionsafecode.com/. It's specifically aimed at C++, but after examining the Swift exception model, I believe the basics apply to Swift as well.

For details on the Swift syntax and error handling model, see the book The Swift Programming Language (Swift 2 Prerelease).


Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to xcode

Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64 iPhone is not available. Please reconnect the device Make a VStack fill the width of the screen in SwiftUI error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65 The iOS Simulator deployment targets is set to 7.0, but the range of supported deployment target version for this platform is 8.0 to 12.1 Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Xcode 10: A valid provisioning profile for this executable was not found Xcode 10, Command CodeSign failed with a nonzero exit code

Examples related to swift

Make a VStack fill the width of the screen in SwiftUI Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Command CompileSwift failed with a nonzero exit code in Xcode 10 Convert Json string to Json object in Swift 4 iOS Swift - Get the Current Local Time and Date Timestamp Xcode 9 Swift Language Version (SWIFT_VERSION) How do I use Safe Area Layout programmatically? How can I use String substring in Swift 4? 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator Safe Area of Xcode 9 The use of Swift 3 @objc inference in Swift 4 mode is deprecated?