Is there a counterpart of CF_SWIFT_NAME() for Objective-C? - objective-c

Is there a way to fix a name of the method implemented in Swift for Objective-C?
A Swift method
static func convert(foo: Foo) -> Bar
in Objective-C becomes
+ (Bar *)convertWithFoo:(Foo *)foo;
while a desired name is
+ (Bar *)convertFoo:(Foo *)foo; // without `With`
Going from Objective-C to Swift I would use the CF_SWIFT_NAME() or NS_SWIFT_NAME() macros.

Yes, you can achieve that with a #objc attribute:
#objc(convertFoo:)
static func convert(foo: Foo) -> Bar { ... }
From "Attributes" in the Swift reference:
The objc attribute optionally accepts a single attribute argument, which consists of an identifier. Use this attribute when you want to expose a different name to Objective-C for the entity the objc attribute applies to. You can use this argument to name classes, enumerations, enumeration cases, protocols, methods, getters, setters, and initializers.

Related

How to implement a swift delegate method in objective-C having parameter?

I have a swift protocol having following delegate method
#objc public protocol CuteDelegate: NSObjectProtocol {
#objc func myCuteFunc(name: NSString)
}
I have declared delegate object in swift as well
weak var delegate : CuteDelegate?
In my objective C controller where I am implementing the above delegate method is as follows
-(void)myCuteFunc:(NSString* )name{
}
But while calling the method in swift controller
self.delegate?.myCuteFunc(name: str as NSString)
I get unrecognized selector sent to instance
Any clue what's the issue
You need to account for the first arguments's name:
Either:
Make your Objective-C function -(void)myCuteFuncWithName:(NSString* )name
Or:
Change your protocol to #objc func myCuteFunc(_ name: NSString) and call it with self.delegate?.myCuteFunc(str)
This is just an artifact of the way Objective-C function names work vs. the way Swift names its arguments. Objective-C has no way of naming the first argument (which is usually described by the function name), so if Swift has a label for the first argument, the convention used is to add With plus the argument name (with the argument name capitalized) to the function name. By adding _, you make the first argument unnamed and that translates better to the Objective-C naming convention.

How do I expose a Swift global function to Objective-C? [duplicate]

the documentation says:
Global constants defined in C and Objective-C source files are automatically imported by the Swift compiler as Swift global constants.
But it doesn't say anything about the other way around. I need to define a global swift constant and be able to see it one the objective c side like a global c constant. Like on the swift side define:
public let CARDS = ["card1", "card2"]
and see use it on the objective c side like
NSLog(#"Cards count: %d", [CARDS count])
What should I do? I've already imported the swift automatically generated header like:
#import "MyProject-Swift.h"
and in Xcode if I command-click on it, it takes me to the correct place in the swift code, but at compile time I get:
'User of undeclared Identifier CARDS'
on my objective c side.
Here is the document about it
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Global variables (including constants) are unaccessible from Objective-C.
Instead, you have to declare a class which has accessors for the global constants.
// Swift
public let CARDS = ["card1", "card2"]
#objc class AppConstant {
private init() {}
class func cards() -> [String] { return CARDS }
}
// Objective-C
NSArray *cards = [AppConstant cards];
Nice answer by #rintaro, but another alternative simple Swift answer for constants that can be used in both Swift and Objective-C:
#objcMembers
class MyConstants: NSObject {
static let kMyConstant1 = "ConstantValue1";
static let kMyConstant2 = "ConstantValue2";
static let CARDS = ["card1", "card2"]
}
You can access this on both Swift and Objective-C by:
MyConstants.kMyConstant1 // this will return "ConstantValue1"
MyConstants.CARDS // this will return array ["card1", "card2"]
Swift global functions (ie. swift top-level functions) cannot be accessed by objC. Period. End of story.
See above answer from rintaro, to wit...
"This excludes Swift-only features such as those listed here:
. . .
Top-level functions defined in Swift"
boom

Implement an Objective-C method with light weight generics in Swift

I have to implement this method in a DataSource protocol of an Objective-C library
(nullable id<SomeClass>)someMethod;
I am trying to implement it in my class in Swift, specifically, the AppDelegate, with what I believe keeps equal the signature
extension AppDelegate: LIBDataSource {
#objc func someMethod<T: SomeClass>() -> T? {
return nil // temporary
}
}
The problem is that
As it is, I have a warning and an error. The error says Method cannot be marked #objc because it has generic parameters (the warning below is also shown)
If I remove #objc, the warning says Non-#objc method 'someMethod()' cannot satisfy optional requirement of #objc protocol LIBDataSource
Is there a way to implement a generic Obj-C method of a Obj-C protocol in Swift? Or do I have to do a separate Objective-C class to accomplish this?
The syntax
id<SomeClass>
is nothing to do with lightweight generics, it means "any Objective-C class as long as it conforms the protocol SomeClass". Your method doesn't need to be generic but it does need to return an object that conforms to the SomeClass protocol. It's signature should probably be something like
func someMethod() -> SomeClass?

Must my swift function's return type inherit from NSObject

I have some swift function in a swift object of type Helper that returns an object of class Parameter. Parameter is defined as:
class Parameter { }
And my function like this:
func getParameter() -> Parameter { }
When accessing the methods from Helper only those that don't return anything show up. However, getParameter is uncallable. My assumption is that its return type is invisible to Obj-C. Must Parameter extend NSObject in order to work?
If you want to have your Swift classes available to Objective-C, they must inherit from an Objective-C bridged class. That's why your ViewControllers are always available, and for free-standing classes, you must make their superclass NSObject. You can also specifically note in code they are bridged by marking them: #objc class Parameter: NSObject{}

How can I make my Objective-C class conform to Swift's `Equatable` protocol?

I have an Objective-C class (that happens to be a button, but that is not important), and at another part of my (mixed language) project, I have an array of these buttons and I'd like to get the index of a button using the find() method. Like so:
func doSomethingWithThisButtonIndex(index:Int)
{
let buttons = [firstButton, secondButton, thirdButton]
if index == find(buttons, firstButton)
{
// we've selected the first button
}
}
but I'm getting the
Type 'ImplicitlyUnwrappedOptional' does not conform to protocol equatable
Okay, so lets go to Objective-C and have ButtonThing implement <Equatable>. But it doesn't recognize that.
So what am I to do?
For now I'm building around it, forcing the array to be an NSArray and using indexOfObject. But this is ugly. And frustrating.
First, in Swift write a custom == operator function for your class.
Second, also in Swift, write a class extension that adds the Equatable protocol conformance.
Perhaps, for example:
func == (lhs: YourClass, rhs: YourClass) -> Bool {
// whatever logic necessary to determine whether they are equal
return lhs === rhs
}
extension YourClass: Equatable {}
And now your class conforms to Equatable, which is Swift specific. You can not do this on the Objective-C end because you can not write custom operators for Objective-C.
If your Objective C class is an NSObject, implement isEqual:
- (BOOL)isEqual:(_Nullable id)other;
This worked for me for Array.index(of: myobject) and == comparisons. NSObject is already Equatable so using a Swift extension does not work.