optional delegates with struct types - swift - ios

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.

Related

Non-'#objc' method does not satisfy requirement of '#objc' protocol [duplicate]

This question already has answers here:
Non-'#objc' method does not satisfy optional requirement of '#objc' protocol
(3 answers)
Closed 1 year ago.
I've put the following into a Playground to try and understand this and I just don't:
import Foundation
#objc protocol Sample {
var value: Int { get set }
func increase()
func decrese()
}
extension Sample {
func increase() {
value += 1
}
func decrease() {
value -= 1
}
}
class Test: Sample {
var value: Int = 0
}
The error appears next to the class declaration for Test saying:
Non-'#objc' method 'increase()' does not satisfy requirement of '#objc' protocol 'Sample'
If I re-declare increase() and decrease() in the class then the warning is silenced. Or also if I remove the declarations from the protocol. Could someone please explain?
EDIT
I do need an Objective-C class to conform to this protocol as well, hence the #objc at the start.
The problem is that you’re defining these methods in a protocol extension. This is used to define a “default implementation” for a protocol (i.e. if a type doesn’t implement the method, the protocol’s implementation will be called).
But Objective-C doesn’t have the concept of default implementations for protocols. So it doesn’t makes sense to declare the protocol as #objc and have default implementations within the Swift protocol extension. An Objective-C class conforming to this protocol would never be able to enjoy these Swift default implementations.
The below code works with empty protocol methods' implementation in the Protocol extension class
import Foundation
protocol Sample {
var value: Int { get set }
func increase()
func decrease()
}
extension Sample {
func increase() { }
func decrease() { }
}
class Test: Sample {
var value: Int = 0
}
or if you want some default implementation of Sample protocol methods in the extension then use
import Foundation
protocol Sample {
var value: Int { get set }
func increase()
mutating func decrease()
}
extension Sample {
func increase() {
print("do anything")
}
mutating func decrease() {
value -= 1
}
}
class Test: Sample {
var value: Int = 0
}
mutating is added before the protocol method decrease() because it modifies the Protocol variable value.
If the Protocol extension doesn't modify any of the Protocol variable (e.g. increase()), then there is no need of mutating keyword

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
}

Delegate a structure in swift?

I am developing an app to increase a little more my knowledge about swift. One of my questions if is it possible to delegate a optional function with a structure as an argument.
What yes Im able to do:
#objc protocol someProtocol {
optional func optionalFunc(someClass: someClass)
}
class someClass: NSObject {
}
But, what I want to do (problems representing a structure in objc):
#objc protocol someProtocol {
optional func optionalFunc(someStructure: someStructure)
}
struct someStructure {
}
And Im not able to find the way to solve this problem.
And the other thing I want, is similar to this but with enums instead of structs:
#objc protocol someProtocol {
optional func optionalFunc(someEnum: someEnum)
}
enum someEnum {
case example
}
If somebody can help me, I will be very grateful!
Lot of thanks! Luciano!
Swift 2.0 lets you do default implementations of protocols.
protocol someProtocol {
func optionalFunc(someStructure: SomeStructure)
}
extension someProtocol {
func optionalFunc(someStructure: SomeStructure){
// optional, leave empty
}
}
struct SomeStructure {
}
This way you can get around using the optional-decoration and do what you wanted.
You cannot pass the parameters as struct or enum, because it's only valid on Swift language, so it cannot be represented in Objective-C.
Another approach, you can declare a function as variable instead of func:
protocol someProtocol {
var optionalFunc: (someStructure) -> ()? { get set}
}
Implementation:
class someClass : someProtocol {
var optionalFunc: (someStructure) -> ()? = { yourStruct in
// Do anything with yourStruct
return
}
}
Using:
var someVar:someClass = someClass()
var result = someVar.optionalFunc(someStructure())
The result is a ()?. If you do not implement the variable, result will nil

Set and protocols in Swift

I would like to initialize a Set with values corresponding to the Hashable protocol and a custom protocol.
I tried :
protocol CustomProtocol: Hashable {}
let set = Set<CustomProtocol>()
But Xcode complains :
Using 'CustomProtocol' as a concrete type conforming to protocol
'Hashable' is not supported
How can I achieve that ?
Thanks in advance.
The immediate reason why you can't do what you want to do is that Hashable is a generic protocol. Thus it — or a protocol that derives from it — cannot be used as a Set's element type. A generic type can used only as a constraint in another generic. You will notice that you can't declare a Set<Hashable> either, even though a set's element type must conform to Hashable.
The simplest approach is to make, not a set of protocols, but a set of some object type. For example, if S is a struct that conforms to CustomProtocol (because it conforms to Hashable plus whatever else CustomProtocol entails), you can declare a set of S.
Example:
protocol CustomProtocol: Hashable {
}
func ==(lhs:S,rhs:S) -> Bool {
return lhs.name == rhs.name
}
struct S : CustomProtocol {
var name : String
var hashValue : Int { return name.hashValue }
}
let set = Set<S>()
If the problem you're trying to solve is that you want a collection of mixed types which are nevertheless in some way equatable to one another, then that is the very same problem solved by protocol extensions, as explained by the discussion in the Protocol-Oriented WWDC 2015 video.
But it would be simpler just to make all your types classes that derive from NSObject. You can still make them adopt some secondary protocol, of course, but the set won't be defined as a set of that protocol but of NSObject.
In Swift 3, one solution is to use the AnyHashable structure.
For instance, to create a Observers/Observable pattern, we could do :
protocol Observer {
func observableDidSomething(_ observable: Observable)
}
class Observable {
private var observersSet: Set<AnyHashable> = []
private var observers: [Observer] {
return observersSet.flatMap { $0 as? Observer }
}
func add<O>(_ observer: O) where O : Observer, O : Hashable {
observersSet.insert(observer)
}
func remove<O>(_ observer: O) where O : Observer, O : Hashable {
observersSet.remove(observer)
}
// ...
private func doSomething() {
// do something ...
observers.forEach { $0.observableDidSomething(self) }
}
}
Notice that I separate the Hashable protocol from my protocol Observer.

Swift Cannot assign to immutable expression of type for protocol enforced variable [duplicate]

I am using swift 2.0, I have a protocol and an extension on the protocol to create a default implementation of a method, the code is as fallows:
protocol ColorImpressionableProtocol {
var lightAccentColor: UIColor? {get set}
var accentColor: UIColor? {get set}
var darkAccentColor: UIColor? {get set}
var specialTextColor: UIColor? {get set}
mutating func adoptColorsFromImpresion(impresion: ColorImpressionableProtocol?)
}
extension ColorImpressionableProtocol {
mutating func adoptColorsFromImpresion(impresion: ColorImpressionableProtocol?){
lightAccentColor = impresion?.lightAccentColor
accentColor = impresion?.accentColor
darkAccentColor = impresion?.darkAccentColor
specialTextColor = impresion?.specialTextColor
}
}
I am later on in my code trying to call this method and am getting an error that reads:
"cannot use mutating member on immutable value:'self' is immutable"
The code is as fallows:
init(impresion: ColorImpressionableProtocol?){
super.init(nibName: nil, bundle: nil)
adoptColorsFromImpresion(impresion)
}
The only thing I can think of is that 'Self' in this case is a protocol, not a class. However I have to be missing something to make this concept work, A default implementation of a method defined by a protocol that edits values also defined by the same protocol.
Thank you for your help and time :)
If you intend to use the protocol only for classes then you can make
it a class protocol (and remove the mutating keyword):
protocol ColorImpressionableProtocol : class {
// ...
func adoptColorsFromImpresion(impresion: ColorImpressionableProtocol?)
}
Then
init(impresion: ColorImpressionableProtocol?){
super.init(nibName: nil, bundle: nil)
adoptColorsFromImpresion(impresion)
}
compiles without problems.
You are adopting this protocol in a class so the self (which is reference type) is immutable. The compiler expects self to be mutable because of the mutable method declared in protocol. That's the reason you are getting this error.
The possible solutions are :
1) Implement a non mutating version of the method where the protocol
being adopted. ie: implement the method in adopting class instead as a
protocol extension.
class MyClass : ColorImpressionableProtocol {
func adoptColorsFromImpresion(impresion: ColorImpressionableProtocol?){
lightAccentColor = impresion?.lightAccentColor
accentColor = impresion?.accentColor
darkAccentColor = impresion?.darkAccentColor
specialTextColor = impresion?.specialTextColor
}
}
2) Make the protocol as class only protocol. This way we can remove the mutating keyword. It's the easiest solution but it can be only used in class.
To make protocol class only :
protocol MyProtocolName : AnyObject { }
OR
protocol MyProtocolName : class { }
3) Make sure only value types adopt this protocol.This may not be useful in
all scenarios.
Here is the detailed explanation and solution for this case.

Resources