Home > Software design >  How to use imported Swift protocols and classes in Objective-C code?
How to use imported Swift protocols and classes in Objective-C code?

Time:09-16

I have a project that bridges Swift and Objective-C both ways, and:

I have a @objc protocol defined in a Swift file:

@objc protocol Plugin {
    @objc func onDevicePackageReceived(np: NetworkPackage) -> Bool
}

All the classes that implement this protocol are also defined in Swift, such as:

@objc class Ping : NSObject, Plugin {
    
    @objc func onDevicePackageReceived(np: NetworkPackage) -> Bool {
        if (np._Type == PACKAGE_TYPE_PING) {
            connectedDevicesViewModel.showPingAlert()
            return true
        }
        return false
    }
    
    @objc func sendPing(deviceId: String) -> Void {
        let np: NetworkPackage = NetworkPackage(type: PACKAGE_TYPE_PING)
        let device: Device = backgroundService._devices[deviceId] as! Device
        device.send(np, tag: Int(PACKAGE_TAG_PING))
    }
}

I've imported the automatically generated bridging header file into the Objective-C .m file that I would like to use the Plugin interface in:

#import "My_App_Name-Swift.h"

The bridging header is definitely working fine since other bridged Swift code are working.

I've also forward declared the protocol in the Objective-C .h header file:

@protocol Plugin;

Now, in the Objective-C .m files, I've got a NSMutableDictionary, _plugins with values that are objects of those classes defined in Swift that implement the Plugin protocol. I want to iterate through each of those objects and call the onDevicePackageReceived(np: NetworkPackage) function as defined in the protocol, and I plan on doing that by confirming the objects to the Plugin protocol which has that function implemented.

for (Plugin* plugin in [_plugins allValues]) {
    [plugin onDevicePackageReceived:np];
}

But it keeps throwing the error Use of undeclared identifier 'Plugin'.

I checked the automatically generated header file and it does appear to have bridged it correctly:

SWIFT_PROTOCOL("_TtP16KDE_Connect_Test6Plugin_")
@protocol Plugin
- (BOOL)onDevicePackageReceivedWithNp:(NetworkPackage * _Nonnull)np        SWIFT_WARN_UNUSED_RESULT;
@end

For further reference, the Dictionary plugins used to be declared in Swift, where the same for loop in Swift works just fine:

for plugin in plugins.values {
    (plugin as! Plugin).onDevicePackageReceived(np: np)
}

So essentially, plugins has now be moved to Objective-C, and I'm trying to replicate this exact for loop in Object-C, with the only difference being that the protocol Plugin and all of the classes that implement it are all declared in Swift, so they need to be properly imported to Objective-C.

CodePudding user response:

This syntax isn't correct for protocols in ObjC:

for (Plugin* plugin in [_plugins allValues]) {

There are no pointers to protocol existentials. ObjC doesn't have existential types like Swift (i.e. a box that contains a type that conforms to a protocol).

I believe you mean:

for (id<Plugin> plugin in [_plugins allValues]) {

This is "an arbitrary ObjC object (id) that conforms to Plugin."

See Working with Protocols in Programming with Objective-C for details on ObjC protocol syntax.

See Protocols as Types in The Swift Programming Language for details on Swift protocol existentials (which are not the same thing as the protocol itself).

CodePudding user response:

Another option if you don't want write objc code is create swift extension for your objc class.
For example if it's called LegacyPluginHolder, add new file LegacyPluginHolder Extensions.swift and create extension:

extension LegacyPluginHolder {

    @objc func notifyAllPlugins(package: NetworkPackage) {
        // Your swift code moves here
    }
}

In LegacyPluginHolder.m you can use extension like this:

[self notifyAllPluginsWithPackage:np]
  • Related