Set and protocols in Swift - ios

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.

Related

Swift 3 Overload Operators for Protocols

I have a protocol that declares property of type Int. I also have few classes that conforms that Protocol, and now I need to overload operator + for all of them. Since operator + will work based on the property declared, I don't want to implement that operator in each class separately.
So I have
protocol MyProtocol {
var property: Int { get }
}
And I would want to have something like
extension MyProtocol {
static func +(left: MyProtocol, right: MyProtocol) -> MyProtocol {
// create and apply operations and return result
}
}
And actually I successfully did that, but trying to work with it I get an error ambiguous reference to member '+'.
When I move operator overload func to the each class separately, issue disappeared, but I'm still looking for a solution to make it works with Protocols.
Solved, by moving func +... outside of Extension, so it is just a method in the file where MyProtocol is declared
protocol MyProtocol {
var property: Int { get }
}
func +(left: MyProtocol, right: MyProtocol) -> MyProtocol {
// create and apply operations and return result
}

How to share an associatedtype between two protocols?

I need to declare two protocols, they both have associatedtypes:
protocol MyView {
associatedtype DataType
associatedtype LayoutType : MyLayout
var data: DataType { get }
var layout: LayoutType { get }
func doLayout()
}
protocol MyLayout {
associatedtype DataType
func computeLayout(with data: DataType?)
}
With this current protocol definition, the associatedtype DataType of MyView is not really the same as the one in MyLayout:
extension MyView {
func doLayout() {
layout.computeLayout(with: self.data)
^^^^^^^^^
Cannot convert value of type 'Self.DataType' to expected argument type '_?'
}
}
The compiler is telling us that the type is not the same.
Is there a way to share an associatedtype between two protocols to fix my issue? Thanks.
The problem is that it's possible for an implementation of MyView to have a LayoutType with a different DataType to its own DataType. Once SE-0142: Permit where clauses to constrain associated types is implemented, you'll just be able to say:
associatedtype LayoutType : MyLayout where LayoutType.DataType == DataType
in order to enforce that conforming types must have a LayoutType with a matching DataType.
However, until this happens, the best you're probably going to be able to do is add the constraint to the extension – which will only make the default implementation of doLayout() available if the DataTypes match.
extension MyView where LayoutType.DataType == DataType {
func doLayout() {
layout.computeLayout(with: data)
}
}

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

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.

How to declare a function with a concrete return type conforming to a protocol?

EDIT:
This question was written before swift added the some keyword, making it obsolete
In objective-c I could declare a method with a return type:
-(UIView<MyProtocol> *)someMethod;
In this example the method returns a UIView that conforms to a protocol MyProtocol.
I want to do something like that in swift:
protocol MyProtocol {
var someProperty : Int {get set}
}
protocol MyDelegate {
func someMethod() -> UIView : MyProtocol // the view should conform to the protocol - I don't care what kind of view it is - I don't want to define a specific type of view
}
In general - The delegate should return a UIView with the var "someProperty"
I don't want to define a concrete UIView class.
I want the user to be able to return any type of UIView (As long as it conforms to the protocol)
The syntax I wrote is invalid - How should I be writing it?
You could just use the protocol as type:
protocol MyDelegate {
func someMethod() -> MyProtocol
}
And use it like this:
protocol MyProtocol {
var someProperty : Int {get set}
}
class CustomView: UIView, MyProtocol {
var someProperty = 2
}
protocol MyDelegate {
func someMethod() -> MyProtocol
}
struct Delegate: MyDelegate {
func someMethod() -> MyProtocol {
return CustomView()
}
}
let delegate = Delegate()
let view = delegate.someMethod()
let property = view.someProperty // property = 2
This is not possible in Swift. Not everything possible in Obj-C has to be possible in Swift. When creating a type requirement you can only combine protocols using the protocol<..., ...> syntax but you can't combine a class and a protocol.
Technically, this should be good for your architecture. You can probably find a workaround but I would advice against it. There is a reason to avoid combining classes with protocols because the interfaces are much more difficult to handle. Most OOP languages don't have that syntax. Many commonly used languages don't even have a syntax to combine protocols.
protocol MyProtocol {
var someProperty : Int {get set}
}
protocol MyDelegate {
func someMethod<T: UIView & MyProtocol>() -> T // the view should conform to the protocol - I don't care what kind of view it is - I don't want to define a specific type of view
}
class MyDelegateTestView : UIView, MyProtocol {
var someProperty: Int = 10
}
class MyDelegateTestClass : MyDelegate {
func someMethod<T>() -> T where T : UIView, T : MyProtocol {
return MyDelegateTestView() as! T
}
}
The question was written before the days of swift-ui
The "some" keyword has solved it by allowing opaque types to be returned from functions
protocol MyDelegate {
func someMethod() -> some MyProtocol
}
Here below is a way.
func myMethod(string: String) -> MyClass:MyProtocol? {
}
You can use without optional type as MyClass: MyProtocol.

Resources