ObjC protocol Implementation in Swift - ios

Ok here is the big problem. I had a library written in ObjC(this). There we had a defined protocol. When I tried to use it in swift file I get constantly:
Type "XXX" does not conform to protocol "XXX"
To simplify things I made up a test project - it should be created as Swift project.
Then create ObjC header file(I called it StupidProtocol.h) with following protocol inside(please note each name and value to exactly match the given including uppercase/lowercase):
#protocol MyProtocol <NSObject>
- (NSString *)getAxisLabel:(id)axis Value:(CGFloat)value;
#end
In bridging header:
#import "StupidProtocol.h"
And then back in Swift file:
class ViewController: UIViewController, MyProtocol
{
func getAxisLabel(axis: AnyObject!, value: CGFloat) -> String! {
return ""
}
}
And baam we got this error again even though auto-complete finishes the getAxisLabel function for me.
I strongly suspect that the problem is with argument "value" and it's function parameter "Value".
Any help will be highly appreciated.
EDIT
Please note that this library is not technically mine, so I cannot change it. I need a way to use it in Swift without changing it's original declaration. My example is only to simplify my problem.
WHAT I TRIED
I have tried following scenarios with no success:
'has different arguments name from those required by protocol' error
func getAxisLabel(axis: AnyObject!, Value value: CGFloat) -> String! {
return ""
}
'has different arguments name from those required by protocol' error
func getAxisLabel(axis: AnyObject!, Value: CGFloat) -> String! {
return ""
}
'type does not conform to protocol'
func getAxisLabel(axis: AnyObject!, Value: CGFloat) -> NSString! {
return ""
}
SOLUTION
See accepted answer

I don't know if this is a bug or not. The Objective-C protocol method
- (NSString *)getAxisLabel:(id)axis Value:(CGFloat)value;
(with uppercase "V" in Value:) is mapped to Swift as
func getAxisLabel(axis: AnyObject!, value: CGFloat) -> String!
(with lowercase "v" in value:), but none of
func getAxisLabel(axis: AnyObject!, value: CGFloat) -> String! { }
func getAxisLabel(axis: AnyObject!, Value: CGFloat) -> String! { }
is accepted by the compiler to satisfy the protocol requirement.
As a workaround, you can annotate the method with an explicit
Objective-C selector name:
class ViewController: UIViewController, MyProtocol
{
#objc(getAxisLabel:Value:)
func getAxisLabel(axis: AnyObject!, value: CGFloat) -> String! {
return ""
}
}

Related

swift protocol conformance with duplicate function names

class A {
func test(string: String, another defaultValue: String = "") {
///...
}
}
class B {
func test(string: String, different defaultValue: Bool = true) {
///...
}
}
protocol Test {
func test(string: String)
}
extension A: Test {
func test(string: String) {
self.test(string: string)
}
}
extension B: Test {
func test(string: String) {
self.test(string: string)
}
}
When I do this I get the following error
Function call causes an infinite recursion
How to confirm to the protocol Test to the classes which have similar function names
When A or B conform to Test, there is no way for the program to know which .test you're calling, cause the internal .test methods in A & B have default values.
To resolve the ambiguity, you can be specific:
extension A: Test {
func test(string: String) {
self.test(string: string, another: "")
}
}
extension B: Test {
func test(string: String) {
self.test(string: string, different: true)
}
}
You have to call class methods with full signature:
extension A: Test {
func test(string: String) {
self.test(string: string, another: "")
}
}
extension B: Test {
func test(string: String) {
self.test(string: string, different: true)
}
}
As swift allows method overloading, you may use the same function name with different signatures.
In your example class methods have different signatures, comparing to the protocol methods, so it is fine. However class methods have default values and can be called without full signature and in this case it is ambiguous as the signature become the same.

Value of Protocol type 'name' cannot conform to protocol 'name' with generic function

I have a function with generic parameters and when it is called I get the error Value of protocol type 'AddableObject' cannot conform to 'AddableObject'; only struct/enum/class types can conform to protocols
I have the following class and protocols
class DataManager{
func updateItem<Item: AddableObject>(itemToUpdate: Item, updateValues: [String : Any], completion: #escaping ((Result<Item, Error>) -> ())){ }
}
protocol AddableObject{ }
This is the code in my VC
class vc: UIViewController{
weak var task: Task?
viewDidLoad(){
super.viewDidLoad()
}
private func uploadTask(){
guard let taskToUpdate = self.task else{return}
//this is where I get the error
DataManager.shared.updateItem(itemToUpdate: taskToUpdate, updateValues: ["index": 1]){result in
}
}
}
This is my task object
class Task: AddableObject{
}
When I try to call the updateItem method from my VC I get the error
Value of protocol type 'AddableObject' cannot conform to 'AddableObject'; only struct/enum/class types can conform to protocols
Why am I getting this error?
One obvious issue: your declaration weak var task: Task? declares an Optional. But the parameter itemToUpdate: is not an Optional. It is a generic type that conforms to AddableObject — and an Optional is not something that conforms to AddableObject.
If we correct for that, and for the other obvious issues in the code you have shown, the resulting code compiles perfectly:
class DataManager{
func updateItem<Item: AddableObject>(itemToUpdate: Item, updateValues: [String : Any], completion: #escaping ((Result<Item, Error>) -> ())){ }
}
protocol AddableObject{ }
class vc: UIViewController{
weak var task: Task?
private func uploadTask(){
guard let taskToUpdate = self.task else {return}
DataManager().updateItem(itemToUpdate: taskToUpdate, updateValues: ["index": 1]){result in
}
}
}
class Task: AddableObject{
}
Thus I have to conclude that the trouble is in code you have not shown us, or that you have misrepresented to us.

optional delegates with struct types - swift

I am getting below error while making protocol as optionals.
Method cannot be marked #objc because the type of the parameter 1
cannot be represented in Objective-C
My code :
#objc protocol PopupDelegate : class {
#objc optional func popupItemSelected(item : PopupItem, identifier : String)
#objc optional func popupItemMultipleSelected(item : [PopupItem], identifier : String)
}
struct PopupItem : Hashable {
var name : String
var id : Int
var isSelected : Bool
init(name: String, id: Int, isSelected : Bool = false) {
self.name = name
self.id = id
self.isSelected = isSelected
}
}
I got one post with same issue in swift 2, but I can't implement this solution as Inheritance not allowed in struct.
I tried to add #objc flag to my struct but got below error
Only classes (and their extensions), protocols, methods, initializers,
properties, and subscript declarations can be declared #objc
Is there any way to implement optional delegates with struct types?
I think the error messages you posted are self explanatory, Struct is not available in Objective-C runtime so when you annotate a protocol with #objc compiler gives you warning that struct can't be passed as an argument to such protocol.
How to achieve optional behaviour in pure swift?
Officially there is no equivalent of objective-C optional in swift. But empty default extensions will help you achieve the same behavior.
protocol PopupDelegate {
func popupItemSelected(item : PopupItem, identifier : String)
func popupItemMultipleSelected(item : [PopupItem], identifier : String)
}
extension PopupDelegate {
func popupItemSelected(item : PopupItem, identifier : String) { }
func popupItemMultipleSelected(item : [PopupItem], identifier : String) { }
}
Now whoever confirms to PopupDelegate need not implement methods as default implementation is already provided and because its empty implementation it's almost same as optional.
One caveat of this approach would be though, if you call respondsToSelector this will return true as there exists a default implementation but with optional you would get appropriate response.

Swift 3.0 Extension - Methods are not getting called in Child class

I have an Extension implementation problem when I am converting my code from swift 2.3 to swift 3.0.
Problem statement: -
1) Each inherited class asking for protocol implementation
2) Not able to call methods which are already implemented in Extension.
Please check below code for more understanding.
protocol MyExtension {
static func addTwoNumber(number1: Double, number2: Double)
}
extension MyExtension {
static func addTwoNumber(number1: Double, number2: Double) {
//implementation
}
static func subTractTwoNumbers(number1: Double, number2: Double) {
//implementation
}
}
internal class firstClass: MyExtension {
static func multiplyTwoNumbers(number1: Double, number2: Double) {
//implementation
// if I call subTractTwoNumbers in this class it is giving me an error
subTractTwoNumbers(10, 10)
//and asking me to implement protocol method as well ie
//static func addTwoNumber(number1: Double, number2: Double)
}
}
Please let me know, what I am doing wrong.
Replace this
subTractTwoNumbers(10, 10)
With
subTractTwoNumbers(number1: 10, number2: 10) // you must have to pass the argument lables name to call the function.
If you don’t want an argument label for a parameter, write an underscore (_) instead of an explicit argument label for that parameter
static func subTractTwoNumbers(_: Double, _: Double) {
//implementation
}
Also, you have written one extra curly braces at the end.
Write function in extension as below
extension MyExtension {
func addTwoNumber(number1: Double, number2: Double) {
//implementation
}
func subTractTwoNumbers(number1: Double, number2: Double) {
//implementation
}
}

How to handle Protocol Delegate when converting Objective-C to Swift

I'm trying to convert a speech recognition code to Swift, Protocol defined in ViewController.h as:
#interface ViewController : UIViewController<SpeechRecognitionProtocol>
{
NSMutableString* textOnScreen;
DataRecognitionClient* dataClient;
MicrophoneRecognitionClient* micClient;
SpeechRecognitionMode recoMode;
bool isMicrophoneReco;
bool isIntent;
int waitSeconds;
}
I got stuck converting below function at ViewController.h:
micClient = [SpeechRecognitionServiceFactory createMicrophoneClient:(recoMode)
withLanguage:(language)
withKey:(primaryOrSecondaryKey)
withProtocol:(self)];
This function is defined in the SpeechSDK.framework as:
#interface SpeechRecognitionServiceFactory : NSObject
/*
#param delegate The protocol used to perform the callbacks/events upon during speech recognition.
*/
+(MicrophoneRecognitionClient*)createMicrophoneClient:(SpeechRecognitionMode)speechRecognitionMode
withLanguage:(NSString*)language
withKey:(NSString*)primaryOrSecondaryKey
withProtocol:(id<SpeechRecognitionProtocol>)delegate;
#end
this protocol looks like this in my converted ViewController.Swift:
import UIKit
protocol SpeechRecognitionProtocol {
func onIntentReceived(result: IntentResult)
func onPartialResponseReceived(response: String)
func onFinalResponseReceived(response: RecognitionResult)
func onError(errorMessage: String, withErrorCode errorCode: Int)
func onMicrophoneStatus(recording: DarwinBoolean)
func initializeRecoClient()
}
class ViewController: UIViewController, SpeechRecognitionProtocol {
var myDelegate: SpeechRecognitionProtocol?
finally I am calling this function inside ViewController.swift. I am getting following error after withProtocol: cannot convert value of type 'SpeechRecognitionProtocol.Protocol' to expected argument type 'SpeechRecognitionProtocol!' :
func initializeRecoClient() {
let language: String = "en-us"
let path: String = NSBundle.mainBundle().pathForResource("settings", ofType: "plist")!
let settings = NSDictionary(contentsOfFile: path)
let primaryOrSecondaryKey = settings?.objectForKey("primaryKey") as! String
micClient = SpeechRecognitionServiceFactory.createMicrophoneClient(recoMode!,
withLanguage: language,
withKey: primaryOrSecondaryKey,
withProtocol: SpeechRecognitionProtocol)
}
You shouldn't declare the SpeechRecognitionProtocol yourself (not sure you added this just for demonstration purposes or whether your actually have that in your code). SpeechRecognitionProtocol is already declared in SpeechRecognitionService.h and available to Swift - this is the one you need to use.
The object implementing that protocol is ViewController. Assuming your initializeRecoClient is a method of that class, the call would need to look like:
micClient = SpeechRecognitionServiceFactory
.createMicrophoneClient(recoMode!,
withLanguage: language,
withKey: primaryOrSecondaryKey,
withProtocol: self)
The SpeechSDK API didn't choose a particularly good name for that factory method.
The withProtocol argument doesn't take the protocol object itself (as the name suggests), but the object implementing the protocol (obviously).
P.S.: Not sure what SpeechAPI version you use, I had to implement those Swift methods to make ViewController conform to SpeechRecognitionProtocol:
func onPartialResponseReceived(response: String!) {}
func onFinalResponseReceived (response: RecognitionResult) {}
func onError (errorMessage: String!,
withErrorCode errorCode: Int32) {}
func onMicrophoneStatus (recording: Bool) {}
func onIntentReceived (result: IntentResult) {}

Resources