Not able to extend ObjC class using Protocol in swift - ios

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.

Related

Swift protocol extension in Objective-C class

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

How to use a parameter with optional type in an optional protocol method

#objc protocol filterDelegate {
#objc optional func appliedFilters(_ filters:[String:AnyObject], withDisplayValues displayValues:[String?]) -> Void
}
The above protocol method gives me an error Method cannot be marked #objc because the type of the parameter 2 cannot be represented in Objective-C
I know this error is due to usage of String? with #objc.
How can I achieve the same functionality of passing String? to an optional protocol method. Is there an alternative for the same?
String data type support only in Swift. So you can use NSString rather than String, which support on both platform.
#objc protocol filterDelegate {
#objc optional func appliedFilters(_ filters:[String:AnyObject], withDisplayValues displayValues:[NSString?]) -> Void
}

swift whats the difference between these protocols

I've seen protocols been declared in two ways, but I don't get the difference.
Ex1:
protocol AddItemViewControllerDelegate {
func controller(controller: AddItemViewController, didAddItem: String)
}
Ex2:
protocol AddItemViewControllerDelegate: class {
func controller(controller: AddItemViewController, didAddItem: String)
}
So whats the difference?
From the docs:
You can limit protocol adoption to class types (and not structures or
enumerations) by adding the class keyword to a protocol’s inheritance
list. The class keyword must always appear first in a protocol’s
inheritance list, before any inherited protocols:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// class-only protocol definition goes here }
Note:
Use a class-only protocol when the behavior defined by that protocol’s
requirements assumes or requires that a conforming type has reference
semantics rather than value semantics.
if you want to declare a variable like this :
var aVar : AddItemViewControllerDelegate?
you have to do :
protocol AddItemViewControllerDelegate: class {
func controller(controller: AddItemViewController, didAddItem: String)
}

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.

AnyObject doesn't recognize proper functions when called

I have a protocol StateMachineDelegate, a class DataSource that conforms to it, and a class StateMachine that has a delegate with such protocol
Both classes implement a function found on the protocol so that if the class has a delegate, let them handle the functions; otherwise the class handles it itself.
StateMachine contains a function like this:
func target() -> AnyObject {
return delegate ?? self
}
My full code goes like this:
import Foundation
#objc protocol StateMachineDelegate {
optional func stateWillChange()
optional func stateDidChange()
optional func missingTransitionFromState(fromState: String?, toState: String?) -> String
}
class StateMachine {
var delegate: StateMachineDelegate?
func target() -> AnyObject {
return delegate ?? self
}
func missingTransitionFromState(fromState: String, toState: String) -> String {
return "Hello"
}
}
class DataSource: StateMachineDelegate {
func missingTransitionFromState(fromState: String?, toState: String?) -> String {
return "Hi"
}
}
When I was running some tests in playground and the StateMachine instance did not possess a delegate the target function returned the same instance as AnyObject. But once I called missingTransitionFromState from target it crashed so I change it to missingTransitionFromState?() with returned nil
Last function line should have returned "Hello"
Once delegate was given the target returned the delegateObject and proceeded to run the function as normal
The playground test are these:
All of your calls to missingTransitionFromState have a ? at the end except for the last one that won't execute. Replacing the ! with a ? fixes the problem. I didn't really understand what the code is doing but the question mark fixes it.
Making the following changes fixes the problem:
Annotating StateMachine's missingTransitionFromState method with dynamic
Changing the parameter types in StateMachine's missingTransitionFromState method from String to String?, to match the signature of the other missingTransitionFromState method.
I believe that the method needs dynamic or #objc in order to be able to be called dynamically using AnyObject. However, after adding that, the compiler will complain that calls to missingTransitionFromState on an AnyObject is ambiguous, because there are two signatures, so you have to fix the signature.

Resources