NSSetUncaughtExceptionHandler not firing in Swift 3? - swift

I'm trying to catch all NSExceptions in a Swift3 program - have been following this Stack Overflow answer
So I've got this:
override func viewDidLoad() {
super.viewDidLoad()
NSSetUncaughtExceptionHandler { exception in
print("EXCEPTION CAUGHT HERE....")
print(exception)
print(exception.callStackSymbols)
}
// force NSException
let array = NSArray()
_ = array.object(at: 99)
}
but the exception's not being called
Instead I get this output:
2016-09-04 14:49:14.252 catchAllErrorsTest[8126:164827] Failed to set
(contentViewController) user defined inspected property on (NSWindow):
*** -[__NSArray0 objectAtIndex:]: index 99 beyond bounds for empty NSArray
I also tried putting the exception into AppDelegate (in applicationWillFinishLaunching and applicationDidFinishLaunching), but same result

I did as follows and works for me in Swift 3.
Define your NSSetUncaughtExceptionHandler as a constant outside any method declaration in your AppDelegate:
let uncaughtExceptionHandler : Void = NSSetUncaughtExceptionHandler { exception in
NSLog("Name:" + exception.name.rawValue)
if exception.reason == nil
{
NSLog("Reason: nil")
}
else
{
NSLog("Reason:" + exception.reason!)
}
}
This fires at any uncaught exception as this constant is evaluated as soon as your App Delegate instance loads i.e. at app launch.
Note: the Void type declaration removes a compiler warning for a Void inferred type that "could be unexpected" ;-) .

Related

Run method on class knowing only its name

how can I launch method knowing its name (as String)
// key.rawValue.firstUppercased is `ApiAddress`
let result = preferencesRepository.perform(Selector("get\(key.rawValue.firstUppercased)"))
where preferencesRepository has method getApiAddress() and conforms to NSObject
public class RealPreferencesRepository: NSObject {
func getApiAddress() -> String
// ...
I have fatal error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[PreferencesModule.RealPreferencesRepository getApiAddress]:
unrecognized selector sent to instance 0x600000090a70'
thanks for help in advance
You need to prefix the getApiAddress() method with #objc attribute.
Also, since the return value of the perform method is Unmanaged, you need to use takeRetainedValue() to convert the return value.
public class RealPreferencesRepository: NSObject {
#objc func getApiAddress() -> String {
return "success"
}
}
let preferencesRepository = RealPreferencesRepository()
let result = preferencesRepository.perform(Selector("getApiAddress"))
let value = result?.takeRetainedValue() as! String
print(value)
// => success

Crashes when calling delegate method inside URLSession closure

I created class named FileTransferManager which manages upload/download task using URLSession.
Because of code's length, I created a gist of my code. : https://gist.github.com/Cyanide7523/eb2f7a743459055e13b8568a51f644f3
And I created delegate protocol to recognize the transfer result.
This is a sample usage of this class :
class SampleViewController: UIViewController, FileTransferDelegate{
let fileMan = FileTransferManager()
fileMan.delegate = self
fileMan.download( /* Some parameters */ )
func fileTransferManager(_ sender: FileTransferManager, didSucceedDownload data: Data, ...) {
print("Download Succeed!")
}
}
But when FileTransferManager calls delegate functions, App always crashes with message "unrecognized selector sent to instance" and I can't figure out why does this crashes.
+++ Error logs
2018-06-27 14:31:57.851160+0900 Project[1428:2194695] -[Project.InitialViewController fileTransferManagerWithSender:willDownload:at:]: unrecognized selector sent to instance 0x10207a0e0
2018-06-27 14:31:57.851783+0900 Project[1428:2194695] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Project.InitialViewController fileTransferManagerWithSender:willDownload:at:]: unrecognized selector sent to instance 0x10207a0e0'
*** First throw call stack:
(0x184d1ad8c 0x183ed45ec 0x184d28098 0x18ee0adb0 0x184d202d4 0x184c0641c 0x1003974b0 0x100399094 0x100396d8c 0x1852a9e4c 0x1852c2b6c 0x185742e88 0x1856848d0 0x185683cac 0x101ec119c 0x101ecd7cc 0x101ec119c 0x101ecd7cc 0x101ecd6b0 0x185744750 0x101ec119c 0x101ece454 0x101eccd44 0x101ed27c8 0x101ed2500 0x18493ffac 0x18493fb08)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Do you implement #objc optional func fileTransferManager(_ sender: FileTransferManager, willDownload contentID: String, at room: String) method in InitialViewController? Also, make your FileTransferManager delegate reference "weak" and remove all force-unwrap's when you call delegate methods (just replace "!" by "?").

Using an Timer to switch a Boolean value?

I'm relatively new to coding and I'm trying to switch a boolean value by with Timer. However, I keep getting an error. Here's my code:
var Display = true
var BoolTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(ThirdViewController.SwitchBool), userInfo: nil, repeats: true)
#objc func SwitchBool()
{
if Display == true{
Display = false
print(Display)
} else {
Display = true
print(Display)
}
}
I get this error when the timer runs out:
[_SwiftValue SwitchBool]: unrecognized selector sent to instance 0x604000646ed0
2018-02-13 10:55:35.664486+1300 HC 1[12286:466779] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue SwitchBool]: unrecognized selector sent to instance 0x604000646ed0'
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Can someone help me understand why I am getting this error and how to fix it?
If you run this code, what would you expect the output to be?
import Foundation
class C: NSObject {
var foo = type(of: self)
}
print(C().foo)
You're expecting to see C, right? But instead you get:
(C) -> () -> C
The reason for this is that when you use self in a property's default value, self refers to a closure which is created as part of the initialization process, not to the object itself. The reason for this is that the property default values are computed before the object's init function has completed, and you can't use self before the object has completed initialization. So, Objective-C is trying to send the SwitchBool method to a closure, which of course doesn't support that method.
To fix this, just make the property lazy; this will cause the property to be initialized sometime after the object itself has been initialized, which will make it possible to use self and have it actually refer to your object, as you can see from the following test:
import Foundation
class C: NSObject {
lazy var foo = type(of: self)
}
print(C().foo)
which outputs:
C
EDIT: If the object in question is a view controller, initializing the timer to nil and creating it in viewDidLoad, as suggested by #vacawama, is also a good approach to take. Basically you just need to make sure you create the timer at some point after the object has completed its initialization.

Realm Swift crashing with uncaught exception when trying to get object

I am using RealmSwift in my MacOS/OSX application on OSX 10.12.3 and Realm crashes with uncaught exception when I try to get an object from the database.
Here's the code snippet that crashes on the get object func:
private var database: Realm!
init(some_var: String) {
var configuration = Realm.Configuration()
configuration.fileURL = configuration.fileURL!.deletingLastPathComponent().appendingPathComponent("\(some_var).realm")
do {
debugPrint("Inside init: Current thread \(Thread.current)")
self.database = try Realm(configuration: configuration)
} catch {
debugPrint("realmInit: Can't create realm database.")
}
}
func getObject<T: Object, K>(with primaryKey: K) -> T? {
debugPrint("Inside getObject: Current thread \(Thread.current)")
return self.database.object(ofType: T.self, forPrimaryKey: primaryKey) // THIS LINE THROWS EXCEPTION
}
I get a crash with an uncaught exception:
"Inside init: Current thread <NSThread: 0x600000075700>{number = 5, name = (null)}"
"Inside getObject: Current thread <NSThread: 0x600000075700>{number = 5, name = (null)}"
libc++abi.dylib: terminating with uncaught exception of type NSException
At first I thought it was a threading issue, but you can see that I init the Realm and getObject on the same thread.
Any help would be appreciated please?
Turns out the issue was with the return type being an optional generic.
After a conversation with one of the contributors to the Realm library on github, it sounded like it could potentially be a RealmSwift bug.
Issue has been raised here https://github.com/realm/realm-cocoa/issues/4951
Workaround is to implement a function that returns a Bool if the object exists, and then return a non-optional generic when it does, while create a new object if it doesn't exist.

Purpose of try! in Swift

I am confused as what exactly is the purpose of try!. The documentation says that you can use try! to call methods that can throw exception but try! is used as a promise from the caller that it will not throw an exception or the developer is not interested in the result of the exception.
So, if the developer is not interested in catching that exception then why even throw the exception from the method in the first case.
func foo() throws {
}
try! foo()
Why not:
func foo() {
}
foo()
struct Error:ErrorType{}
// function is defined somewhere
// i know just the declaration
//
// func foo(i:Int!) throws -> Void
//
func foo(i: Int!) throws {
if i == nil {
throw Error()
}
}
// i am sure the call is 'safe'
// this simplifies my code
try! foo(2)
//foo(2) // error: call can throw but is not marked with 'try'
let str = "2,2" // mimic result of some operation
do {
// i am not sure if str represents an Int
try foo(Int(str))
} catch {
print("error")
}
A function could throw errors if it gets illegal parameters. However, if this function is used with an UI that allows only the input of legal parameters, then you can call this function with try!, because you are sure that the exception is never thrown.
I read a book on Swift that says you can use try! in the early stages of development, because the app will crash if an error is thrown, so a developer or tester cannot overlook this error. However, I wouldn't use this, because it could be forgotten to remove a try! before the code goes into production.