Crashes when calling delegate method inside URLSession closure - swift

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 "?").

Related

Tuple as function parameter gives "Unrecognized selector sent..."

I have a function declared like this:
func rspGetCategories(_ response: (Int, [String:Any])) {
I try to call it like this:
self.perform(act, with: (tag, outjson))
Where:
act = Selector(("rspGetCategories:"))
tag = 1
outjson = ["status":"ServerError"]
I just get an "unrecognized selector sent...". What am I missing here?
Full error message:
2018-07-18 11:20:15.852755+0200 Appname[8071:4529543] -[Appname.ViewController rspGetCategories:]: unrecognized selector sent to instance 0x10380be00
2018-07-18 11:20:15.853361+0200 Appname[8071:4529543] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Appname.ViewController rspGetCategories:]: unrecognized selector sent to instance 0x10380be00'
*** First throw call stack:
(0x18418ed8c 0x1833485ec 0x18419c098 0x18e27edb0 0x1841945c8 0x18407a41c 0x1020f5dfc 0x1020ebc3c 0x102f811dc 0x102f8119c 0x102f85d2c 0x184137070 0x184134bc8 0x184054da8 0x186039020 0x18e071758 0x1020fec34 0x183ae5fc0)
libc++abi.dylib: terminating with uncaught exception of type NSException
As the selector method should be representable by Objective-C so with #objc, we can not pass tuple as parameter for a method otherwise compiler will throw this error,
Method cannot be marked #objc because the type of the parameter cannot
be represented in Objective-C
One possible solution is as below,
#objc func rspGetCategories(_ response: [String: Any]) {
print("Tag: \(response["tag"] as! Int) Status:\(response["status"] as! String)")
}
And construct the response as below,
let selector = #selector(rspGetCategories(_:))
let tag = 1
let response: [String: Any] = ["tag": tag,
"status": "ServerError"]
self.perform(selector, with: response)
Another solution is to pass two parameters instead of tuple. As below,
#objc func rspGetCategories(_ tag: NSNumber, response: [String: Any]) {
print("\(tag.intValue) \(response)")
}
let selector = #selector(rspGetCategories(_:response:))
let tag = 1
let response = ["status": "ServerError"]
self.perform(selector, with: tag, with: response)
Remember that selector method argument type needs to be a reference type.
Split your tuple into two parameters like
#objc func rspGetCategories(_ response: Int, dict: [String: Any]) {
and change the selector
let act = #selector(Test.rspGetCategories(_:dict:))
Test is the name of my class, replace it with your class name

uncaught exception 'NSInvalidArgumentException', reason: '-[SubResultComponent setDate:]: unrecognized selector sent to instance 0x60000046b640'

I have an error to which I am not sure on how to fix.
Here is there error
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SubResultComponent setDate:]: unrecognized selector sent to instance 0x60000046b640'
Here is the SubResultComponent
extension SubResultComponent {
#nonobjc public class func fetchRequest() -> NSFetchRequest<SubResultComponent> {
return NSFetchRequest<SubResultComponent>(entityName: "SubResultComponent")
}
#NSManaged public var date: String?
#NSManaged public var price: NSDecimalNumber?
}
This is where I Call set date
func updateUI() {
guard rateLog != nil else {
return
}
// Update cell UI
self.dateLabel.text = rateLog?.date
self.priceLabel.text = numberFormatter.string(from: (rateLog?.price)!)
}
#NSManaged acts similar to the objective c version of dynamic; it is a promise to the compiler that the object will have those properties at runtime. It does not actually make those properties. For this to work these properties need to be setup correctly in the core data model file. Using the editor's "Create NSManagedObject Subclass..." option greatly helps in keeping you code and the model in sync.

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.

XCSourceEditorCommandInvocation swift extension causes "unrecognized selector sent to instance" exception

When I try and add swift extensions to classes in XcodeKit (framework for adding Xcode extensions) the compiler is happy to build without any errors, but when the code runs I get the following exception:
-[XCSourceEditorCommandInvocation test]: unrecognized selector sent to instance 0x7fc60543f2b0
Below is sample code which can reproduce the exception:
class SourceEditorCommand: NSObject, XCSourceEditorCommand {
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: #escaping (Error?) -> Void ) -> Void {
// Call extension method
invocation.test() // <--- Exception thrown here
completionHandler(nil)
}
}
extension XCSourceEditorCommandInvocation {
func test() {
print("it works!")
}
}
I've extended ObjC classes in swift in the past without any issues so I'm a bit stuck here.
I've tried:
adding #objc before the method declaration.
adding public to the extension and method.
I'm not extending a class cluster so it's likely not this question.
I'm not extending a protocol so it's likely not this question.
Neither Objective-C categories nor Swift extensions are supported on the classes or types in XcodeKit.framework at this time.

NSSetUncaughtExceptionHandler not firing in Swift 3?

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" ;-) .