Swift: Property conforming to a specific class and in the same time to multiple protocols - swift

In Objective-C, it's possible to write something like that:
#property(retain) UIView<Protocol1, Protocol2, ...> *myView;
But how can I write this code in swift?
I already know how to make a property conform to many protocols, but it does not work by using the inheritance:
var myView: ??? protocol<Protocol1, Protocol2, ...>
Edit:
I use many UIView subtypes like UIImageView, UILabel or others, and I need to use some of the UIView properties plus some methods defined in the protocols. In the worst case I could create a UIViewProtocol with the needed properties, but I would know if it is possible in Swift to declare a property/variable with a type and some protocol to conform with.

You can do this with a generic class using a where clause:
A where clause enables you to require that an associated type conforms
to a certain protocol, and/or that certain type parameters and
associated types be the same.
To use it, make the class your property is defined in a generic class with a type constraint to check if the type parameter for your property matches your desired base class and protocols.
For your specific example, it could look something like this:
class MyViewController<T where T: UIView, T: Protocol1, T: Protocol2>: UIViewController {
var myView: T
// ...
}

In Swift 4 it's finally possible. You can declare variable of some class conforming to protocol at the same time, like this:
class ClassA {
var someVar: String?
}
protocol ProtocolA {}
class ClassB {
var someOptional: (ClassA & ProtocolA)? // here is optional value
var some: ClassA & ProtocolA // here is non-optional value; need to provide init though :)
}

One and probably a bit ugly one of the ways to do that, is to create a wrapper protocol for UIView:
protocol UIViewRef {
var instance: UIView { get }
}
Now it is possible to create a protocol which implements Protocol1, Protocol2 and UIViewRef, which is going to be used to get the UIView itself:
protocol MyUIViewProtocol: UIViewRef, Protocol1, Protocol2 { }
And last step will be implementing UIViewRef protocols for your UIViews, which, in you case, as I understand, already implement Protocol1 and Protocol2:
// SomeOfMyViews already implements Protocol1 and Protocol2
extension SomeOfMyUIViews: MyUIViewProtocol {
var instance: UIView { return self }
}
As the result we have MyUIViewProtocol, implementers of which hold a reference to a UIView and each of them implement
Protocol1 and Protocol2. One caveat though - to get the UIView itself, we need to ask it's reference from instance
property. For example
// Lets say we're somewhere in a UIViewController
var views: [SomeOfMyUIView] = // Get list of my views
views.forEach { self.view.addSubview($0.instance) }

Related

What's the point of conforming to an empty protocol in Swift

I'm just working on a codebase where the most classes conform to an empty protocol. Is there any point of doing so? Actually conforming that protocol does nothing.
Edit as reaction to #vacawama's helpful comment : the protocol is only used once
import Foundation
protocol myUrlRequestConfig {}
extension myUrlRequest: myUrlRequestConfig {
public static func configGetAppConfig() -> URLRequest {
let request = ....
return request
}
}
An empty protocol can be useful if you want to create an easy way to add static functions or variables to several different classes. Of course, this only makes sense if the static functionality is the same for all classes conforming to the protocol.
Here is an example: I have a protocol sizeable. Every class that conforms to Sizeable is extended with the static variable stride and size.
protocol Sizeable { }
extension Sizeable {
static var size: Int {
return MemoryLayout<Self>.size
}
static var stride: Int {
return MemoryLayout<Self>.stride
}
}
class Foo: Sizeable {}
class Baa: Sizeable {}
Foo.size
Baa.size
Our app has three perfectly legitimate uses for an empty protocol:
The protocol has an extension with method definitions. The implementation in the extension is automatically injected into any type that adopts the protocol.
The protocol has other protocols that adopt it. This allows multiple protocols to be unified under a single head.
The protocol marks the adopter as wanting some other type to behave in a certain way. Code can always is whether an object is ThisEmptyProtocol and if it is, can behave differently than if it isn't.

How to do?I want a protocol that can only be followed by two classes

I want a protocol that can only be followed by two classes。(ClassA or ClassB).
protocol MyProtocol where Self: ClassA || ClassB {
}
This requirement almost certainly indicates a design problem. If any piece of your system cares what is implementing the protocol, then the protocol does not capture the whole interface. For example, if at any point you use as? ClassA, then this is a dangerous use of protocols.
It is impossible in Swift to have a protocol that I can see but that I cannot implement. There are languages like Go where it's possible (though still not in exactly the way you describe), but it's impossible in Swift today. As #MartinR notes, the answer by J. Doe doesn't actually change anything. Outside parties can still just implement both protocols.
With that said, it's possible to achieve things very similar to what you're describing.
First, and most obviously: use access controls.
private protocol MyProtocol {}
public class ClassA: MyProtocol {}
public class ClassB: MyProtocol {}
This works if all the classes and protocol are in one file. If not, put them all in a framework and use internal rather than private. Now nothing outside this file/module can implement MyProtocol. The limitation is that MyProtocol is also can't be seen outside the file/module.
This can be resolved by lifting a struct:
public struct MyStruct: MyProtocol {
private let value: MyProtocol
public init(classA: ClassA) { value = classA }
public init(classB: ClassB) { value = classB }
// Methods to conform to MyProtocol by forwarding to value
}
With this, it's impossible to generate a MyStruct that is initialized with anything but a ClassA or ClassB.
Alternately, if you really literally mean "ClassA or ClassB" that's not a struct or a protocol. That's a enum:
enum MyEnum {
case classA(ClassA)
case classB(ClassB)
}

Setting a delegate generates a compile error

I want to use a strategy pattern to register a set of objects that implement a protocol. When I set this up, I get a compile error when trying to set the delegate that is part of the protocol.
For discussion purposes, I have slightly reworked the DiceGame from the Swift eBook's Delegation chapter. The changes of significance are:
protocol DiceGame - declares a delegate
class SnakesAndLadders implements DiceGame (and therefore the protocol and delegate)
class Games holds 3 instances of SnakesAndLadders as
1) a concrete class of SnakesAndLadders
2) a 'let' constant of protocol DiceGame
3) a 'var' variable of protocol DiceGame
We can set the delegate fine if we use the concrete class (snakesAndLadders). However, there is a compile error if we use 'let' to hold it as a protocol (diceGameAsLet) but it compiles if we hold the variable as a 'var' (diceGameAsVar).
It is easy to work around, however, the delegate itself never changes so should be held as a 'let' constant, as it is only the internal property that changes. I must not understand something (possibly subtle but significant) about protocols and how they work and should be used.
class Dice
{
func roll() -> Int
{
return 7 // always win :)
}
}
protocol DiceGame
{
// all DiceGames must work with a DiceGameDelegate
var delegate:DiceGameDelegate? {get set}
var dice: Dice {get}
func play()
}
protocol DiceGameDelegate
{
func gameDidStart( game:DiceGame )
func gameDidEnd( game:DiceGame )
}
class SnakesAndLadders:DiceGame
{
var delegate:DiceGameDelegate?
let dice = Dice()
func play()
{
delegate?.gameDidStart(self)
playGame()
delegate?.gameDidEnd(self)
}
private func playGame()
{
print("Playing the game here...")
}
}
class Games : DiceGameDelegate
{
let snakesAndLadders = SnakesAndLadders()
// hold the protocol, not the class
let diceGameAsLet:DiceGame = SnakesAndLadders()
var diceGameAsVar:DiceGame = SnakesAndLadders()
func setupDelegateAsClass()
{
// can assign the delegate if using the class
snakesAndLadders.delegate = self
}
func setupDelegateAsVar()
{
// if we use 'var' we can assign the delegate
diceGameAsVar.delegate = self
}
func setupDelegateAsLet()
{
// DOES NOT COMPILE - Why?
//
// We are not changing the dice game so want to use 'let', but it won't compile
// we are changing the delegate, which is declared as 'var' inside the protocol
diceGameAsLet.delegate = self
}
// MARK: - DiceGameDelegate
func gameDidStart( game:DiceGame )
{
print("Game Started")
}
func gameDidEnd( game:DiceGame )
{
print("Game Ended")
}
}
DiceGame is a heterogeneous protocol that you're using as a type; Swift will treat this type as a value type, and hence (just as for a structures), changing its mutable properties will mutate also the instance of the protocol type itself.
If you, however, add the : class keyword to the DiceGame protocol, Swift will treat it as a reference type, allowing you to mutate members of instances of it, without mutating the instance itself. Note that this will constraint the protocol as conformable to only by class types.
protocol DiceGame: class { ... }
With the addition of the above, the mutation of immutable diceGameAsLet:s properties will be allowed.
In this context, it's worth mentioning that the : class keyword is usually used to constrain protocols used as delegates (e.g., DiceGameDelegate in your example) as conformable to only by class types. With this additional constraint, the delegates can be used as types to which the delegate owner (e.g. some class) only hold a weak reference, useful in contexts where a strong reference to the delegate could create a retain cycle.
See e.g. the 2nd part of this answer for details.
The issue is that when you store something as a Protocol, even if it is a class, swift considers them to be a value type, instead of the reference type you are expecting them to be. Therefore, no part of it is allowed to be changed. Take a look at this reference for more information.

A way to inherit from multiple classes

I have two classes I want to use in my new class. The first one implements a swipe to delete and the second enables a long press gesture:
class DeleteItem: UITableViewCell {
}
class OpenDetail: UITableViewCell {
}
Since Swift doesn't allow a class to inherit from multiple classes the following example obviously won't work:
class ItemViewCell: DeleteItem, OpenDetail {
}
So in order to create ItemViewCell and having both options, I'll have to have one of the classes to inherit from each other:
class DeleteItem: UITableViewCell {
}
class OpenDetail: DeleteItem {
}
class ItemViewCell: OpenDetail {
}
The problem is, if I only want the long press gesture I'll have to create a new class without inheriting from DeleteItem. Is there a better way of doing this?
This is the perfect case for using Protocols and Protocol extension. A swift protocol is like an interface in Java for example. A protocol can define a set of functions which has to be implemented by the entities which want to conform to this protocol, moreover a protocol can define properties which has to be present in these entities too. For example:
protocol ItemDeleter {
var deletedCount: Int {get set}
func deleteItem(item: ItemType)
}
The problem is, that each entity would have to provide its own implementation of func deleteItem(item: ItemType) even if multiple entities share the same logic of deleting an item, this where a protocol extension comes in handy. For example:
extension ItemDeleter {
func deleteItem(item: ItemType) {
// logic needed to delete an item
// maybe incremented deletedCount too
deletedCount++
}
}
Then you could make your ItemViewCell conform to the ItemDeleter protocol, in this case all you need is to make sure that ItemViewCell has a property deletedCount: Int. It does not need to provide an implementation for func deleteItem(item: ItemType) as the protocol itself provides a default implementation for this function, however you can override it in your class, and the new implementation will be used. The same applies for DetailOpener protocol.

Define property that must be a subclass AND conform to protocol

In Swift 2.0, how can I do the equivalent of #property (nonatomic, strong) NSManagedObject*<SomeProtocol> model?
Basically, I’m trying to define a property on my class that must both be a subclass of NSManagedObject AND conform to SomeProtocol (I will be calling methods defined by both).
I saw this: https://stackoverflow.com/a/25826948/363789, but I'm not sure how I can apply this syntax to property definition...
Swift 4
This is now possible in Swift 4 using the following syntax:
var myObject: NSManagedObject & SomeProtocol
Unfortunately Swift doesn't support such type composition yet.
Three reasonable good solutions as workaround (the third one is probably the best):
1.
You can make another type with has those properties and all types have to inherit from it in order to be used as model.
class SomeManagedObject: NSManagedObject, SomeProtocol {
// conforming to protocol
func someMethod()
}
// type declaration
var model: SomeManagedObject
2.
A more static way to solve this problem is to use generics:
class Aclass<T: NSManagedObject where T: SomeProtocol > {
var model: T
}
Note: You don't have to care about another type which has to be the superclass but this solution is not as dynamic and abstract as the one before.
3.
You could also make your own protocol and make NSManagedObject conform though an extension:
protocol ManagedProtocol {
// if you want to access some methods/properties directly from the type
func method()
var variable: Int { get }
// otherwise call all methods/properties through "managedObject"
// this property also reduces casting if you want to have the object itself
var managedObject: NSManagedObject { get }
}
extension NSManagedObject: ManagedProtocol {
var managedObject: NSManagedObject { return self }
}
Now the type is abstract and can be written as:
var model: protocol<ManagedProtocol, SomeProtocol>