Swift protocol extension in Objective-C class - ios

I have a protocol written in Swift that should be conformed by several controllers and some of them are written in Objective-C. Not all of them need all methods from this Swift protocol so at first I decided to provide some methods with default implementation for making them 'optional' but in this case my Objective-C controllers don't recognize my Swift protocol. Did anyone face the same situation and did find a solution? Some sample of my code:
#objc public protocol SwiftProtocol: AnyObject {
func requiredMethod()
func optionalMethod()
}
extension SwiftProtocol {
func optionalMethod() {}
}
#interface ObjClass ()<SwiftProtocol>
And I've got the error : (59, 1) Cannot find protocol declaration for 'SomeProtocol'
Using #objc public in methods instead of extension gave the same result.
TIA for your help!

Objective-C protocols cannot have default implementations.
Objective-C protocols can have real optional methods/properties, unlike Swift protocols, which only have required methods/properties. The workaround for this in Swift is the use of a default implementation, however, sadly those cannot be seen in Objective-C.
I would suggest creating a pure Swift protocol and for all Objective-C classes that want to extend this, write the conformance in Swift, then create #objc wrapper functions in Swift that call the default protocol implementations - if it needs to be called, if it doesn't need to be called, simply ignore it.
Something along the lines of:
protocol SwiftProtocol {
func requiredFunc()
func optionalFunc()
}
extension SwiftProtocol {
func optionalFunc() {}
}
#objc extension ObjcClass: SwiftProtocol {
#objc func requiredFunc() {
print("do something")
}
// This will call the default implementation - can be omitted if you don't need to call the default implementation from Objective-C
#objc func objc_optionalFunc() {
optionalFunc()
}
}

Related

How some iOS frameworks define Swift protocols using optional without #objc?

As mentioned in the title, some standard Swift protocols get away with declaring optional requirements without the use of #objc. For example:
public protocol UIScrollViewDelegate : NSObjectProtocol {
#available(iOS 2.0, *)
optional public func scrollViewDidScroll(_ scrollView: UIScrollView) // any offset changes
#available(iOS 3.2, *)
optional public func scrollViewDidZoom(_ scrollView: UIScrollView) // any zoom scale changes
...
}
public protocol UIApplicationDelegate : NSObjectProtocol {
...
#available(iOS 2.0, *)
optional public func applicationDidBecomeActive(_ application: UIApplication)
#available(iOS 2.0, *)
optional public func applicationWillResignActive(_ application: UIApplication)
...
}
The following test failed with the expected compile error due to the missing #objc:
public protocol Test: NSObjectProtocol {
optional func test()
}
Optional protocol method options are available only for Objective-C based protocol. UIScrollViewDelegate and UIApplicationDelegate are Objective-C based protocol. That means you don't need to say its #objc. The code which you seeing are bridged file between Objective-C and Swift. So that Swift can have access to those methods. Your protocol Test is defined in Swift. Even though you adapting NSObjectProtocol the class not exposed to Objective-C runtime, where the optional option is available. For this reason, you need to explicitly mention this protocol is exposed to Objective-C with #objc.
I guess that is because the UIScrollViewDelegate and UIApplicationDelegate are Objective-C classes bridged on Swift for you.
The #objc bridges Swift to Objective-C https://stackoverflow.com/a/30795237/440299
You can define optional protocol methods by using extensions
important :
Both protocol and extension should be public to be accessible outside of the framework
public protocol MyProtocol {
func method()
}
public extension MyProtocol {
func method() {}
}

Override of instance method form extension depends on deprecated inference of '#objc'

I am trying to convert my code (written in Swift 3) to Swift 4, for that I am adding #objc where needed. Xcode has done quite a good job to automatically fix them but I am struggling with a few (all using the same 2 methods), where Xcode can't help, it just puts #objc somewhere in my code.
I am overriding a method called navbarRightButtonAction(button:) like this in my ViewController class.
class ViewController: PBViewController {
override func navbarRightButtonAction(button: PBAdaptiveButton) {
...
}
}
This is where I get the warning saying:
Override of instance method 'navbarRightButtonAction(button:)' from extension of PBViewController depends on deprecated inference of '#objc'
Then I thought the problem us be in the PBViewController class which looks like this:
extension PBViewController: PBNavigationBarDelegate {
func navbarRightButtonAction(button: PBAdaptiveButton) {
print("Override this method")
}
}
So I added #objc func navbarRightButtonAction(button: PBAdaptiveButton) but it didn't help.
Then I looked into the PBNavigationBarDelegate protocol
protocol PBNavigationBarDelegate {
func navbarRightButtonAction(button:PBAdaptiveButton)
}
I added #objc protocol PBNavigationBarDelegate but it didn't help either.
I have no other idea what to do to fix the deprecation warning.
Put #objc or #nonobjc in front of the extension:
#objc extension PBViewController: PBNavigationBarDelegate
Take a look at Limiting #objc Inference, SE-0160 at Swift Evolution for more details. It contains the following example regarding extensions:
Enabling/disabling #objc inference within an extension
There might be certain regions of code for which all of (or none of) the entry points should be exposed to Objective-C. Allow either #objc or #nonobjc to be specified on an extension. The #objc or #nonobjc will apply to any member of that extension that does not have its own #objc or #nonobjc annotation. For example:
class SwiftClass { }
#objc extension SwiftClass {
func foo() { } // implicitly #objc
func bar() -> (Int, Int) // error: tuple type (Int, Int) not
// expressible in #objc. add #nonobjc or move this method to fix the issue
}
#objcMembers
class MyClass : NSObject {
func wibble() { } // implicitly #objc
}
#nonobjc extension MyClass {
func wobble() { } // not #objc, despite #objcMembers
}

Not able to extend ObjC class using Protocol in swift

I am trying to extend an ObjC class for dependency injection using a protocol in swift , this class isn't owned by me so I cant make changes. For one of the method its working but for other Xcode always says that the class isn't confirming to the protocol and suggests me to add the method to the extension
Below is the declaration in ObjC header
- (BOOL) subscribeToTopic:(NSString *)topic
QoS:(AWSIoTMQTTQoS)qos
extendedCallback:(AWSIoTMQTTExtendedNewMessageBlock)callback;
Here is the protocol and its extension
protocol PopAWSIoTDataManagerProtocol {
func publishString(_ data:String, onTopic:String, QoS:AWSIoTMQTTQoS) -> Bool
func subscribeToTopic(_ topic: String, qoS: AWSIoTMQTTQoS, extendedCallback: (NSObject, String, Data) -> Void) -> Bool
}
extension AWSIoTDataManager : PopAWSIoTDataManagerProtocol {
}
Notice the error below it suggests me exactly same func to be added to extension which I already have added to main protocol
Not sure whats wrong , as I was able to add another method just fine.
Article used as a reference is this https://medium.com/flawless-app-stories/the-complete-guide-to-network-unit-testing-in-swift-db8b3ee2c327
You must confirm protocol PopAWSIoTDataManagerProtocol, cuz it extension of class AWSIoTDataManager
Or you can try this
extension PopAWSIoTDataManagerProtocol where Self: AWSIoTDataManager {
...
}
The protocol that you have defined has all function's to implemented as mandatory.
You need to define them as optional. Either you define them as optional or implement all of them which was the error image that you have attached is asking for.
#objc protocol PopAWSIoTDataManagerProtocol {
#objc optional func publishString(_ data:String, onTopic:String, QoS:AWSIoTMQTTQoS) -> Bool
#objc optional func subscribeToTopic(_ topic: String, qoS: AWSIoTMQTTQoS, extendedCallback: (NSObject, String, Data) -> Void) -> Bool
}
Default behavior of Protocol, when implemented is that you have to override all the methods. These are categorized as Protocol Requirements.
There are Optional Protocol Requirements , which do not have to be implemented by types that conform to the protocol.
To achieve Optional Protcol Requirements, the protocol and the requirement i.e the optional function must be marked as #objc.

How to override instance method from protocol extension in Swift? [duplicate]

This question already has an answer here:
Implement protocol through extension [duplicate]
(1 answer)
Closed 6 years ago.
I'm trying to override an instance method from a protocol extension, and I'm having some trouble.
For context, I'm making an iOS app with a lot of different UICollectionViews. These views get data from different databases (requiring different callback funcs) and have very different layouts. Because any combination of (database, layout) is possible, it's difficult to make a nice OOP class hierarchy without massive code duplication.
I had the idea to put the layout functions (mostly those defined in the UICollectionViewDelegateFlowLayout protocol) into protocol extensions, so I can decorate a given UICollectionView subclass with a protocol that's extended to implement all relevant layout functions, but I'm having a hard time of it. The essence of the problem is contained in the code below.
class Base {
func speak(){
print("Base")
}
}
class SubA: Base, ProtocolA {}
class SubB: Base, MyProtocolB {}
protocol MyProtocolA{
func speak()
}
protocol MyProtocolB{
func speak()
}
extension MyProtocolA{
func speak(){
print("A")
}
}
extension MyProtocolA{
func speak(){
print("B")
}
}
let suba = SubA()
suba.speak() // prints "Base", I want it to print "A"
let subb = SubB()
subb.speak() // prints "Base", I want it to print "B"
Thoughts?
The default implementations in the protocols are only called if the class that conforms to these protocols do not implement that method itself. The classes' methods override the default implementations of the protocols, not the other way around.
Typically, you'd do something like:
protocol MyProtocolA {
func speak()
}
protocol MyProtocolB {
func speak()
}
extension MyProtocolA {
func speak() {
print("A")
}
}
extension MyProtocolB {
func speak() {
print("B")
}
}
class SubA: MyProtocolA {}
class SubB: MyProtocolB {}
let suba = SubA()
suba.speak() // prints "A"
let subb = SubB()
subb.speak() // prints "B"
But if you do
class SubC: MyProtocolA {
func speak (){
print("C")
}
}
let subc = SubC()
subc.speak() // prints "C"
Frankly, as you look at this, the use of Base is entirely redundant in this example, so I've removed it. Clearly, if you need to subclass from Base for other reasons, feel free. But the key point is that protocol default implementations don't override the classes' implementation, but rather the other way around.

Using Swift Protocol Inheritance

I am trying to create a delegate that uses traditional polymorphism to compensate for a device being bluetooth LE, bluetooth, etc and can't seem to get the syntax right for casting.
Here is my parent protocol and class:
#objc protocol DeviceDelegate
{
func didConnectToDevice(name:String)
func didFailToConnectToDevice(name:String)
func didDisconnectFromDevice(name:String)
func didWriteData(data:NSData)
func didReceiveData(data:NSData)
}
class Device: NSObject
{
var delegate: DeviceDelegate?
}
Now here is the child class and protocol simplified down:
protocol BluetoothDelegate : DeviceDelegate
{
func didFindService(name:String)
func didFindCharacteristic(name:String)
}
class BLE: Device
{
func someFunc()
{
let bluetoothDelegate = (delegate as? BluetoothDelegate)
bluetoothDelegate.didFindService(UUIDString)
}
}
It throws the following error on the first line of that function:
Cannot downcast from 'DeviceDelegate?' to non-#objc protocol type 'BluetoothDelegate'
This doesn't make sense to me since it should allow casting to a child like a usual object does.
If I put #objc in front of BluetoothDelegate I get the following error:
#objc protocol 'BluetoothDelegate' cannot refine non-#objc protocol 'DeviceDelegate'
Anybody have any ideas on this?
When I copy your code and paste it directly into a playground and add #objc in front of your BluetoothDelegate definition, I get a message on this line:
bluetoothDelegate.didFindService("asdf")
'BluetoothDelegate?' does not have a member named 'didFindService'
Because you have used as?, there is a chance that bluetoothDelegate is nil. You should be using optional chaining here. Replacing with the following line reports no errors in the playground, indicating that you may have done something else in your code that you're not showing us.
bluetoothDelegate?.didFindService("asdf")
Alternatively, you could use this:
if let bluetoothDelegate = delegate as? BluetoothDelegate {
bluetoothDelegate.didFindService(UUIDString)
}
The message you're seeing about DeviceDelegate not being an objc protocol indicates to me that you have written these in two different files and maybe forward-declared DeviceDelegate incorrectly.

Resources