5

I want to integrate a Swift class into an UIViewController class. I think I made all settings correct. I have a class rURLTask:NSObject with a function:

@objc public func primer() {
    print("primer")
}

In my .swift file and can call it from my Objective-C-class with:

[URLTask primer];

and it prints nicely. Another function is:

@objc public func ladeURL(url: URL?) {
    print("loadURL")
}

but this one I cannot call from Objective-C. I try to write:

NSURL* testURL = [NSURL URLWithString:@"http://www.google.com"];    
[URLTask ladeURL:testURL];

I get the error:

No visible @interface for 'rURLTask' declares the selector 'ladeURL:'

I think there is a very basic mistake. Using Objective-C in Swift 3 in another project worked well.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
heimi
  • 499
  • 7
  • 16
  • Control-click on the "project-Swift.h" file and check how the functions are imported to Swift. – Or try autocompletion. – Martin R May 31 '18 at 19:06
  • See [swift 3 method in objective-c fails with no visible @interface for 'MySwiftClass' declares the selector 'addX:andY'](https://stackoverflow.com/q/41080100/1187415) – the same approach works in your case. – Martin R May 31 '18 at 19:16
  • `URLTask` is not a good name for a variable, in ObjectiveC or in Swift. Variable names should start with a _small letter_. – matt May 31 '18 at 19:44

2 Answers2

13

The reason you cannot call

@objc public func ladeURL(url: URL?) {
    print("loadURL")
}

by saying

[URLTask ladeURL:testURL];

is that ladeURL: is not the name of this method as far as Objective-C is concerned. That is what the compiler means when it says that "No visible @interface for 'rURLTask' declares the selector ladeURL:".

Its name as far as Objective-C is concerned is ladeURLWithUrl:. That is because you have exposed the url: parameter name as an external label.


If it was important to you to be able to say

[URLTask ladeURL:testURL];

in Objective-C, you could have declared ladeURL like this:

@objc public func ladeURL(_ url: URL?) {
    print("loadURL")
}

See the underscore? That hides the external label of the parameter.


Another solution, allowing you to keep the url: external label, would be to declare the Objective-C name as part of the objc attribution:

@objc(ladeURL:) public func ladeURL(url: URL?) {
    print("loadURL")
}

That says to the compiler: "I know you would like to translate the name of this method into Objective-C as ladeURLwithUrl:, but don't; translate it as ladeURL: instead."

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Some tips for how Swift and Objective-C see one another's method names are to be found in my discussion here: http://www.apeth.com/swiftBook/apa.html#_objective_c_methods – matt May 31 '18 at 20:04
  • Excellent answer (As usual.) Voted. – Duncan C May 31 '18 at 20:33
  • @DuncanC Thanks. It did occur to me to mention that the rules here (for translation of method names between the two languages) have changed over time; I think in Swift 2 the OP's original code would have worked, am I remembering right? But I decided it wasn't worth flogging that horse. – matt May 31 '18 at 21:55
2

When you import the class to OC , the name of the method written in swift is translated concatenated with the withParameterType , as prime method

@objc public func primer() {
  print("primer")
}

has no parameters it can be called like this

[URLTask primer];

but this

@objc public func ladeURL(url: URL?) {
   print("loadURL")
}

is translated to

[URLTask ladeURLWithUrl:<#NSURL#>];
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87