Encountering issues when using a protocol based Enum - swift

A simplified version of what I currently have, taken from a Playground file I setup:
import Foundation
/// Simplified protocol here
protocol MyProtocol: CaseIterable {
static var count: Int { get }
var name: String { get }
}
/// Simplified extension. This works fine with app
extension MyProtocol {
static var count: Int {
return Self.allCases.count
}
}
/// Simplified enum, this works fine as well
enum MyEnum: MyProtocol {
case value
var name: String {
return "name"
}
}
Using the following works as expected:
print(MyEnum.count) // 1
let myEnum = MyEnum.value
print(myEnum.name) // name
However, I would like to create an object that is initialized with MyEnum.
First, I attempted the following:
final class MyManager {
private let myEnum: MyProtocol
init(myEnum: MyProtocol) {
self.myEnum = myEnum
}
}
However, both spots where I use MyProtocol provide the following error:
Protocol 'MyProtocol' can only be used as a generic constraint because
it has Self or associated type requirements
I then switched it up with the following that got rid of the error, but produces a new issue:
final class MyManager<MyProtocol> {
private let myEnum: MyProtocol
init(myEnum: MyProtocol) {
self.myEnum = myEnum
}
}
When I attempt to access properties for myEnum, they do not appear in Xcode:
I need to be able to access the properties defined in MyProtocol, but neither approach is working properly for me & I have run out of ideas.

The MyProtocol generic is not the same as the MyProtocol protocol. You need something like this
final class MyManager<MyP: MyProtocol> {
private let myEnum: MyP
init(myEnum: MyP) {
self.myEnum = myEnum
print(myEnum.name)
}
}
I also want to point out that a better way to implement count is by extending CaseIterable
extension CaseIterable {
static var count: Int {
return Self.allCases.count
}
}

Related

swift5 - enum protocol - Self and self and a variable

I like to separate soms function and var from a enum and thought this was a way. (Just sample code)
This results in a compile error "Type 'Self' has no member 'allCases'"
public protocol EnumFunctions: Hashable {
static var numOfCases: Self { get }
}
public extension EnumFunctions {
static var numOfCases: Self {
return self.allCases.count
}
}
my Enum Cooking Timer
public struct Cook_Time {
// struct naming for the dump command like
// dump(Cook_Time(), name: Cook_Time().structname)
let structname : String = "Cook_Time"
let a = "a"
let allValues = PFTime.allValues
public enum PFTime : String , CaseIterable, EnumFunctions {
case t1m = "1mim"
case t3m = "3min"
case t5m = "5min"
case t10m = "10min"
....
static let allValues = PFTimer.allCases.map { $0.rawValue }
}
}
How can I fix this? what is my false mind setting about this? I do need Self instead of self, rigth?
Also if I make an extension for PFTime, in a separated file, why does I get the error "Cannot find type 'PFTime' in scope"?
In order to access allCases property of CaseIterable protocol, you need to change your EnumFunctions protocol to something like this:
public protocol EnumFunctions: Hashable, CaseIterable {
static var numOfCases: Int { get }
}
public extension EnumFunctions {
static var numOfCases: Int {
return allCases.count
}
}
Also you can create an extension to PFTime just by adding Cook_Time. as prefix because PFTime is placed inside Cook_Time struct:
extension Cook_Time.PFTime {
}

swift protocol extension default implementation vs actual implementation in class

Consider the following code:
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
}
// does not specify its own name
class MyClass: MyProtocol {
}
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
}
let myClass = MyClass()
print("\(MyClass.name)")
//>>"unnamed"
let myClass2 = MyClass2()
print("\(MyClass2.name)")
//>>"Specific name"
Does swift guarantee that for classes (such as in this case MyClass2) that have an actual implementation of a protocol property, in this case "name", that this is used from the class, and not the one of the default "name" implementation via the protocol extension?
Having a default implementation for a required protocol function/property means that your conforming types won't have to implement that function/property, they can use the default implementation instead.
However, if a conforming type does implement the function/property, then the compiler will always call the more specific implementation, namely the one in your conforming class and not the default one.
So even if you stored an instance of MyClass2 in a variable of type MyProtocol, you'd still get the MyClass2 implementation when accessing the property on the variable.
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
The behaviour is different for non-required properties/functions declared and defined in a protocol extension. If you declare a property/function in the protocol extension only, then even if you provide a different implementation for that in a conforming class, you won't be able to access that implementation from a variable whose type is the protocol type rather than the specific conforming type.
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
// Optional protocol requirement
static var nonRequired: String {
return "nonRequired"
}
}
// does not specify its own name
class MyClass: MyProtocol { }
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
// Specific implementation
static var nonRequired: String {
return "Specific"
}
}
let myClass = MyClass()
MyClass.name
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
type(of: myClass2).nonRequired // "nonRequired"
MyClass2.nonRequired // "Specific"

Create class instances from an array of metatypes

Let's suppose I do have a protocol TestProtocol and classes TestClass1 and TestClass2 conforming to TestProtocol:
protocol TestProtocol: AnyObject { }
class TestClass1: TestProtocol { }
class TestClass2: TestProtocol { }
Suppose further that I do have a metatype array defined like
var testArray: [TestProtocol.Type] = [TestClass1.self, TestClass2.self]
How can I create instance objects of TestClass1 and TestClass2 based on the entries of testArray?
p.s.: This should be done without creating a switch construct that checks agains TestClass1.self and TestClass2.self like
var instanceArray: [TestProtocol] = []
for element in testArray {
if element == TestClass1.self {
instanceArray.append(TestClass1())
} else if element == TestClass2.self {
instanceArray.append(TestClass2())
}
}
I looked into generics, but did not find a suitable solution, because the metatypes are stored in an array of type [TestProtocol.Type].
This works:
protocol TestProtocol: AnyObject {
init()
}
final class TestClass1: TestProtocol { }
final class TestClass2: TestProtocol { }
var testArray: [TestProtocol.Type] = [TestClass1.self, TestClass2.self]
for testType in testArray {
let testInstance = testType.init()
}
The final classes could be structs as well (but you need to remove the AnyObject constraint)
Can you let me know if it compiles for you? If not, you need to update your toolchain.

Multiple enum implementing protocols questions

I defined enums as confirming to a protocol Eventable:
protocol Eventable {
var name: String { get }
static var all: [Eventable] { get }
}
enum MyEnum: String, Eventable {
case bla = "bla"
case blu = "blu"
var name: String {
return self.rawValue
}
static var all: [Eventable] {
return [
MyEnum.bla,
MyEnum.blu
]
}
}
I have other enums like MyEnum also under the form:
enum Bla: String, Eventable {
}
I have two questions:
for the enums having a String data type, I would like to avoid duplicating the generation of the variable name:
var name: String
I am not sure how to write that in Swift. I tried to play around with the "where" clause but not success. How can I achieve that?
when I write my enums and conform to the protocol for that part:
static var all: [Eventable] { get }.
I would like that for the enum MyEnum, it constrains the variable to:
static var all: [MyEnum] { ... }
because for now I can put in the returned array any element being an Eventable and it's not what I need.
Amongst other things, I tried to define a generic constraint in the protocol for it, but I get the following error:
Protocol 'Eventable' can only be used as a generic constraint because
it has Self or associated type requirements
Thank you very much for the help!
For your second question, you just need to use Self:
protocol Eventable {
var name: String { get }
static var all: [Self] { get }
}
Self, similar to self, just means "the current type".
The first question is a little bit harder because you can't really get all the values of an enum safely. See here for more info. The closest I got was:
extension Eventable where Self: RawRepresentable, Self.RawValue == String {
var name: String {
return self.rawValue
}
}
This means that you can omit the declaration of name in MyEnum, but not all.
For the part of the question we need to extend Eventable protocol where Self inherits RawRepresentable
protocol Eventable {
var name: String { get }
static var all: [Self] { get }
}
extension Eventable where Self: RawRepresentable {
var name: Self.RawValue {
return self.rawValue
}
}
enum MyEnum: String, Eventable {
case bla = "bla"
case blu = "blu"
static var all: [MyEnum] = [bla, blu]
}
For the second part of your question we need to configure the function to handle a generic type
I would suggest making the function generic as well here is an example
func printEnum<T: Eventable>(_ event: T) {
print(event.name)
}
and we can use it for any object confirms to Eventable
printEnum(MyEnum.bla)

Conformance to a protocol (as a protocol)

Say I have a protocol Item, and a struct ConcreteItem that conforms to it.
protocol Item {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
}
At some point I want to have two sets of ConcreteItem.
let set1 = Set([ConcreteItem(name: "item1")])
let set2 = Set([ConcreteItem(name: "item2"), ConcreteItem(name: "item1")])
Which I'd expect to return the item with name "item1".
I can make ConcreteItem conform to Hashable and the Set code will work. However, lets say I also had the following:
struct AnotherConcreteItem: Item {
let name: String
}
I'd like AnotherConcreteItem to also conform to Hashable simply for having conformed to Item.
However, when I try to implement that idea:
extension Item: Hashable {
var hashValue: Int {
return name.characters.count
}
}
I get the following error: Extension of protocol 'Item' cannot have an inheritance clause.
Extension of protocol 'Item' cannot have an inheritance clause
Here item is the protocol so conforming Hashable protocol will not work. For more details Refer here
What you are trying to do is possible with some protocols, but not all. If the protocol you are trying to conform to does not have any associated types or Self, this is possible.
Example:
protocol A {
func foo()
}
protocol Item: A {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
}
extension Item {
func foo() {
}
}
Everything that conforms to Item will also conform to A.
However, Hashable has Self constraints. To conform to Hashable, you must also conform to Equatable. To conform to Equatable, the implementation of == must be in a concrete class, not another protocol because Equatable can't be used as a parameter type. The most you can do is something like this:
protocol Item: Hashable {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
// you need to implement == in every concrete class
static func ==(lhs: ConcreteItem, rhs: ConcreteItem) -> Bool {
return ...
}
}
extension Item {
var hashValue: Int {
return name.characters.count
}
}