Is it possible to call methods defined in a protocol extension in Swift from Objective-C?
For example:
protocol Product {
var price:Int { get }
var priceString:String { get }
}
extension Product {
var priceString:String {
get {
return "$\(price)"
}
}
}
class IceCream : Product {
var price:Int {
get {
return 2
}
}
}
The price string of an instance of IceCream
is '$2' and can be accessed in Swift, however the method is not visible in Objective-C. The compiler throws the error 'No visible @interface for 'IceCream' declares the selector ...'.
In my configuration, if the method is defined directly in the Swift object's implementation, everything works as expected. i.e.:
protocol Product {
var price:Int { get }
var priceString:String { get }
}
class IceCream : Product {
var price:Int {
get {
return 2
}
}
var priceString:String {
get {
return "$\(price)"
}
}
}
You can write a Swift extension and use it in Objective-C code.
Mitrenegades solution is to use an objective-c protocol, is one way, but if you want a swift protocol, then the other would be to refactor the code so as to not use the objective-c class directly, but instead use the protocol (e.g. some protocol based factory pattern). Either way may be appropriate for your purposes.
In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions. Extensions can add new functionality to a type, but they can't override existing functionality.
I am nearly certain the answer to this is "no", although I haven't found official Apple documentation that spells it out.
Here is a message from the swift-evolution mailing list discussing the proposal to use dynamic dispatch for all method calls, which would provide calling semantics more like Objective-C:
Again, the sole exception to this is protocol extensions. Unlike any other construct in the language, protocol extension methods are dispatched statically in a situation where a virtual dispatch would cause different results. No compiler error prevents this mismatch. (https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001707.html)
Protocol extensions are a Swift-only language feature, and as such are not visible to objc_msgSend()
.
If it is ok to remove the priceString
from the protocol, and only have it in your extension, you can call the protocol extension by casting IceCream
to a Product
in a helper extension.
@objc protocol Product { var price:Int { get } } extension Product { var priceString:String { return "$\(price)" } } // This is the trick // Helper extension to be able to call protocol extension from obj-c extension IceCream : Product { var priceString:String { return (self as Product).priceString } } @objc class IceCream: NSObject { var price: Int { return 2 } }
Protocol extension does not work with @objc protocol, however you can extend the class in swift as a workaround.
@objc protocol Product {
var price: NSNumber? { get }
var priceString:String { get }
}
...
// IceCream defined in Objective-C that does not extend Product
// but has @property(nonatomic, strong, nullable) (NSNumber*)price
// to make it confirm to Product
...
extension IceCream: Product {
var priceString:String {
get {
return "$\(price ?? "")"
}
}
}
This code is not clean at all, but it works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With