Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c

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)"
        }
    }
}
like image 861
Anthony Mattox Avatar asked Sep 24 '15 13:09

Anthony Mattox


People also ask

Can I use Swift extension in Objective-C?

You can write a Swift extension and use it in Objective-C code.

Can Objective-C class conform to Swift protocol?

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.

Can protocols be extended Swift?

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.


3 Answers

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().

like image 152
Steve Madsen Avatar answered Sep 28 '22 09:09

Steve Madsen


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     } } 
like image 20
andershqst Avatar answered Sep 28 '22 10:09

andershqst


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.

like image 37
Freeman Man Avatar answered Sep 28 '22 08:09

Freeman Man