2

I'm trying to use a global error handling in my project as follow:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    NSSetUncaughtExceptionHandler { (exception) in
        print(exception)
        let myView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
        let errorLabel :UILabel = UILabel(frame: CGRect(x: 10, y: (UIScreen.main.bounds.height/2) - 25 , width: UIScreen.main.bounds.width - 20 , height: 50))
        errorLabel.text = "Exception"
        myView.addSubview(errorLabel)
        self.window?.addSubview(myView)
  }
    return true
}

But I get this error while coding:

A C function pointer cannot be formed from a closure that captures context.

I searched a lot but I couldn't find any answer. Is there anything else I need to do?

UPDATE:

I found out that the problem is because I'm declaring variables inside the block. How should I handle showing a view while exception happens?

Niloufar
  • 512
  • 1
  • 5
  • 24
  • Your code *does compile* (if the missing closing bracket is added). I assume that there is more in the closure, which causes the problem. – Compare https://stackoverflow.com/q/33260808/1187415. – Martin R Jan 19 '19 at 11:48
  • @MartinR There is no missing bracket in my original code, sorry for missing it here. no I can't compile – Niloufar Jan 19 '19 at 11:51
  • That (still) can not be your real code, because the `return true` is missing. – I have create a new iOS project in Xcode 10.1 and added `NSSetUncaughtExceptionHandler { (exception) in print(exception) }` to the didFinishLaunchingWithOptions method, and it compiles without problems. – Martin R Jan 19 '19 at 11:54
  • @MartinR I'm using xcode 10.1 and I forgot to write the return true here which I just edited. but still I get the error I said. I just found this link "https://stackoverflow.com/questions/25441302/how-should-i-use-nssetuncaughtexceptionhandler-in-swift" but I don't understand the answer. – Niloufar Jan 19 '19 at 11:58
  • @Niloufar Same for me. Created a new Xcode project and everything works as expected. – André Slotta Jan 19 '19 at 12:05
  • Oh!! I'm declaring a UIButton inside my block which causes the problem and when I delete that code it works fine. I will edit the question so you can see what I mean. – Niloufar Jan 19 '19 at 12:08
  • As I suspected: The explanation is in https://stackoverflow.com/q/33260808/1187415. – Martin R Jan 19 '19 at 12:14
  • @MartinR Would you please explain that a little for me. I don't understand – Niloufar Jan 19 '19 at 12:20
  • You simply cannot use any reference to `self` in the exception handler. Note also that the exception handler can be called on any thread, therefore creating UI elements might cause undefined behavior. Finally note that it catches only Objective-C exceptions, and does not prevent the program from crashing. – Martin R Jan 19 '19 at 12:29
  • @MartinR Thanks, What should I do if I want to show a specific message whenever an exception happens? – Niloufar Jan 19 '19 at 12:36
  • @Niloufar, just don't use any label and don't add any subViews from your exception handler closure. That is not such a good idea. If you are using it to debug, just use the debugger area with your print statement instead. It will give you the same information and you won't have the error – Galo Torres Sevilla Jan 19 '19 at 12:36
  • @Niloufar: Most runtime exceptions cannot be catched, or “handled” in a way that the program can continue. – Martin R Jan 19 '19 at 12:40
  • @MartinR I want to handle it for when I get null values from my API which I managed them with default values, but to be on a safe side I want to prevent app from crash. – Niloufar Jan 19 '19 at 12:43
  • 1
    @Niloufar: You'll have to handle that directly where you read/decode/whatever ... the API response, with the usual tools (check for nil, optional binding, optional casts, guard ...). – A global exception handler is not the right tool for that purpose. – Martin R Jan 19 '19 at 12:45
  • @MartinR ok Thanks :) – Niloufar Jan 19 '19 at 12:46

1 Answers1

3

To summarize the above discussion: There are various problems with your approach:

  • The argument to NSSetUncaughtExceptionHandler is a C function pointer. You can pass a closure in Swift, but only if the closure does not capture any context. In your case, the closure cannot use self. The solution from How to use instance method as callback for function which takes only func or literal closure? cannot be used here because NSSetUncaughtExceptionHandler() has no “context” parameter.

  • Even if you manage to reference the application delegate in the exception handler: It can be called on any thread, so that it can be undefined behavior to call any UI related methods, such as adding views or labels.

  • NSSetUncaughtExceptionHandler catches only Objective-C exceptions, but not Swift errors (from throw) or Swift runtime exceptions (such as “unexpectedly found nil”).

  • Even if an exception is caught, you cannot “handle” it in a way that the program continues afterwards. It will still crash.

You wrote in a comment

I want to handle it for when I get null values from my API ... but to be on a safe side I want to prevent app from crash.

An exception handler is not the right tool for that purpose. Use optional binding, optional casts, guards, etc to handle the response and to avoid runtime exceptions.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382