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

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.

Related

Invoking a static swift function of an extended class from ObjC++ [duplicate]

This question already has answers here:
Objective-C Category Causing unrecognized selector
(8 answers)
Closed 5 months ago.
I have a swift class containing a lot of methods. I've split it into multiple classes using the extension keyword.
Taking an example,
#objc
class SwiftModule : NSObject {
#objc
static func Method1 (_ pArg:UnsafeMutablePointer<NSString>) -> Void {
// Does something
}
#objc
static func Method2 (_ pArg:UnsafeMutablePointer<NSString>) -> Void {
// Does something
}
}
#objc
extension SwiftModule {
#objc
static func Method3 (_ pArg:UnsafeMutablePointer<NSString>) -> Void {
// Does something
}
}
Now, I have to invoke this method from C++ using the ObjC++ wrapper. I have the following in a .mm file,
// Include the generated swift interface header to invoke swift methods from C++
#include "GeneratedSwiftInterfaceHeader.hpp"
bool CppModule::InvokeMethod1 () {
NSString * string;
[SwiftModule Method1:&string];
if ([string length] == 0)
return false;
// Do something with string
return true;
}
bool CppModule::InvokeMethod2 () {
NSString * string;
[SwiftModule Method2:&string];
if ([string length] == 0)
return false;
// Do something with string
return true;
}
bool CppModule::InvokeMethod3 () {
NSString * string;
[SwiftModule Method3:&string];
if ([string length] == 0)
return false;
// Do something with string
return true;
}
I'm able to invoke Method1 and Method2. But Method3 failed with the following error
ibc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[SwiftModule Method3:]: unrecognized selector sent to class 0x107343398'
terminating with uncaught exception of type NSException
(lldb)
This means that Method3 is not recognised as part of SwiftModule class, right? Why? Where did I go wrong?
I have seen a few posts (this and this) which showcase a similar problem, but it still doesn't work.
I'm using swift 5.0 and Xcode 13.4.
When discussing the problem in details in the comments section, it turned out that the Objective-C interfaces generated were part of a static library which the final project is linked against.
There is a known issue with Objective-C categories inside of static libraries, where the categories methods implementation are not "glued" to their symbolic names by linker. In order to get it round you pass -ObjC flag to the linker in the application (not library) target build settings. It will make the linker to load all symbols of static libraries found in both Objective-C classes and categories:

Xcode project keeps crashing after converting to Swift 4, "Class implemented in both..."

I recently converted an Xcode project to Swift 4. I fixed all the syntax errors. However, my app now crashes, after outputting an error message which starts
Class MPExportableArtworkProperties is implemented in both...
I have been looking around S/O but all I find is that its related to MapBox which I don't even have in my project/podfile. Can it have something to do with the regular map-kit? It crashes the app when I open the tab with my map, so I'm guessing that's where the problem is.
Full error message, formatted for readability:
objc[24634]: Class MPExportableArtworkProperties is implemented in both
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/MediaPlaybackCore.framework/MediaPlaybackCore
(0x126b1b108) and
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/MediaPlayer.framework/MediaPlayer
(0x1258107d0).
One of the two will be used. Which one is undefined.
This is the plowerthingscontroller saying that you are logged in
libc++abi.dylib: terminating with uncaught exception of type
NSException
I had this exact error, and in the end found it was due to something completely different: a circular dependency. Consider:
class DependencyManager {
static let shared = DependencyManager()
let aDependency: SomeDependency
init() {
aDependency = SomeDependency()
}
func resolve() -> SomeProtocol {
// Create the actual class
return 0
}
}
where SomeDependency requires SomeProtocol
class SomeDependency {
let x: SomeProtocol
// Me trying to be clever and auto-inject the dependency
init(x: SomeProtocol = DependencyManager.shared.resolve()) {
self.x = x
}
}
So when you first access DependencyManager.shared it tries instantiate SomeDependency, which requires DependencyManager.shared to already be instantiated.
I have no idea why MPExportableArtworkProperties is mentioned in the error, however cleaning up the code fixed the issue for me.

Ambiguous reference when using selectors

Ready to use example to test in Playgrounds:
import Foundation
class Demo {
let selector = #selector(someFunc(_:))
func someFunc(_ word: String) {
// Do something
}
func someFunc(_ number: Int) {
// Do something
}
}
I'm getting an Ambiguous use of 'someFunc' error.
I have tried this answer and getting something like:
let selector = #selector(someFunc(_:) as (String) -> Void)
But still getting the same result.
Any hints on how to solve this?
Short answer: it can't be done.
Long explanation: when you want to use a #selector, the methods need to be exposed to Objective-C, so your code becomes:
#objc func someFunc(_ word: String) {
// Do something
}
#objc func someFunc(_ number: Int) {
// Do something
}
Thanks to the #objc annotation, they are now exposed to Objective-C and an compatible thunk is generated. Objective-C can use it to call the Swift methods.
In Objective-C we don't call a method directly but instead we try to send a message using objc_msgSend: the compiler is not able to understand that those method are different, since the generated signature is the same, so it won't compile. You will face the error:
Method 'someFunc' with Objective-C selector 'someFunc:' conflicts with previous declaration with the same Objective-C selector.
The only way to fix it is to have different signatures, changing one or both of them.
The selector is obviously ambiguous, neither methods have external parameter names. Remove the empty external parameters to solve this:
#objc func someFunc(word: String) {
// Do something
}
#objc func someFunc(number: Int) {
// Do something
}
After that, you should specify a selector with the parameter name:
#selector(someFunc(word:))
or
#selector(someFunc(number:))

Selector to method inside singleton

This sounds like a stupid question, but I have been trying to find a solution for hours now, and I still don't know what to do. I am using Swift 3.0, and I am having an issue calling a method inside a singleton class from a selector inside another class. My singleton class is as follows:
class Singleton : NSObject {
static let sharedInstance = Singleton()
private override init() {} // defeats instantiation
func myAction() {
// do something useful...
}
}
Then, here is the class from which I am calling the method contained in the Singleton:
class StatusBarPresenter {
func addItemsToMenu(menu: NSMenu) {
...
menu.insertItem(withTitle: "Disconnect this network",
action: #selector(Singleton.sharedInstance.myAction),
keyEquivalent: "D", at: 4)
...
}
}
Xcode doesn't complain about the code... it compiles without any errors or warnings, but the selector doesn't work. The UIMenuItem that I add to the menu is disabled, which means that the selector is not working. If the selector instead calls a method inside the class, everything works fine just as usual. This is a screenshot of what I am getting:
Thanks to Martin R. for pointing out that in my code I was not setting an explicit target for the UIMenuItem, leading to it being nil and ultimately self.
The following line added to the addItemsToMenu function after the call to insertItem solves the problem:
menu.item(at: 4)?.target = Singleton.sharedInstance

Confused by Ambiguous Reference Member Reference error in Swift

Given this workspace example:
import Foundation
import CoreBluetooth
class Something: NSObject, CBPeripheralDelegate {
var peripheral:CBPeripheral!
func peripheral(peripheral: CBPeripheral, didUpdateValueForDescriptor descriptor: CBDescriptor, error: NSError?) {
}
func foobar() {
self.peripheral.writeValue([], forDescriptor: 0) // I use a real value instead of 0 in real code
}
}
I get errors that look like:
Playground execution failed: Playground2.playground:6:3: error: ambiguous reference to member 'peripheral'
self.peripheral.writeValue([], forDescriptor: 0)
^~~~
Playground2.playground:5:6: note: found this candidate
var peripheral:CBPeripheral!
^
Playground2.playground:1:7: note: found this candidate
func peripheral(peripheral: CBPeripheral, didUpdateValueForDescriptor descriptor: CBDescriptor, error: NSError?) {
So, it can't seem to decide if my self.peripheral code is a reference to my variable named peripheral or one of the delegate functions I've chosen to implement? I guess I could rename my peripheral variable to be something else...
But what surprises me is that if I construct what seems like a similar example, it has no issues disambiguating:
import Foundation
extension Int {
func frobnicate() { }
func barf() { }
}
class YakAttack: NSObject {
var something:Int!
func something(something:Int, else:Int) {
}
func foobar() {
self.something.frobnicate()
}
}
The foobar() function should have the same issue with the self.something reference, but it has no such problem. What's the difference?
(I am using XCode 7.3 Beta 5, which has the latest Swift version in it)
My best guess is it's a message passing confusion due to dealing with Objective-C protocols/datatypes. I'm not really qualified to analyze the specific, but thus far I've learned there are still some weird bugs like this that come up when dealing with both languges.
I'm guessing this will be fixed down the road, but for now you'll probably just have the change the variable name.