swift5 - enum protocol - Self and self and a variable - swift

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 {
}

Related

Return specific property when accessing a Struct without referencing its name

Is there a way in Swift that I can get a property from a struct without having to reference it?
For example
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
It's possible by adopting CustomStringConvertible and constraining T also to CustomStringConvertible
public struct ASValue<T: Hashable>: Hashable, CustomStringConvertible where T: CustomStringConvertible {
private let id = UUID()
public var item: T
public var description : String {
return "\(item)"
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
And this is a struct, the init method is for free.
What you are looking for is exactly provided with property wrappers in swift. You have to make your ASValue structproperty wrapper by applying #propertyWrapper to the declaration:
#propertyWrapper
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var wrappedValue: T
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
Then you can use this wrapper to wrap any property in another type and access that property directly. You can even access the ASValue wrapper type by prefixing with _:
class SomeClass {
#ASValue var val = "Example"
func printVal() {
// print(_val) for actual value
print(val) // Prints: Example
}
}
You can also pass this property with its wrapper identity to other function:
func printVal<T: Hashable>(#ASValue val: T) {
// print(_val) for actual value
print(val) // Prints: Example
}
let obj = SomeClass()
printVal(val: obj.val)
If for some constraint you can't use the property wrapper approach you can try the #dynamicCallable attribute as a workaround. This allows values created from your types to be invoked as methods with specified arguments.
#dynamicCallable
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
// need to have withArguments with argument type confirming
// to ExpressibleByArrayLiteral
func dynamicallyCall(withArguments arg: [Int] = []) -> T {
return item
}
}
let val = ASValue(item: "Example")
print(val()) // Prints: Example

Encountering issues when using a protocol based Enum

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
}
}

Protocol Conformance Check

How can I perform conformance check against protocol with AssociatedType. Xcode shows error:
Protocol 'MyListener' can only be used as a generic constraint because
it has Self or associated type requirements
My ultimate goal is to extract "MyListener.section" from an array of weakObjects, where the handler matches the function argument.
Note. The NSPointerArray of weakObjects is suppose to capture different types of MyListeners.
public class MyHandler<O,E> {
var source = [O]()
var dest = [E]()
}
public protocol MyListener:class {
var section: Int {get}
associatedtype O
associatedtype E
var handler: MyHandler<O,E>? { get }
}
public class MyAnnouncer {
private let mapWeakObjects: NSPointerArray = NSPointerArray.weakObjects()
public func add<L: MyListener>(listener: L) {
let pointer = Unmanaged.passUnretained(listener).toOpaque()
mapWeakObjects.addPointer(pointer)
}
public func search<O, E> (h:MyHandler<O,E>) -> [Int] {
_ = mapWeakObjects.allObjects.filter { listener in
if listener is MyListener { // Compilation failed
}
if let _ = listener as? MyListener { //Compilation error
}
if listener is MyListener.Type { //Compilation failed
}
}
return [] // ultimate goal is to extract corresponding [MyListener.section].
}
}
Unfortunately, Swift doesn't support protocols with AssociatedType to conformance.
You should try to use Type Erasure. One of the way is to implement type erasure by creating new AnyType class.
Here is another way to release type erasure (example from the internet)
protocol SpecialValue { /* some code*/ }
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController { // Now we can perform conformance
print(sc.typeErasedCurrentValue)
}

Swift Protocol Extension - Can't Access Func

If I have a series of protocols like so:
protocol Customer {
var name:String { get set }
var age: Int { get set }
var startDate:Date { get set }
var meals:Array<String> { get set }
var market:Int { get set }
}
protocol Vegan:Customer {
}
protocol Vegetarian:Customer {
}
protocol Paleo:Customer {
}
and extension like so:
extension Customer where Self:Vegan, Self:Vegetarian {
func getMeals() -> Array<String> {
return ["VeganMeal1", "VeganMeal2", "VeganMeal3", "VeganMeal4"]
}
}
extension Customer where Self:Vegetarian {
func getMeals() -> Array<String> {
return ["VegetarianMeal1", "VegetarianMeal2", "VegetarianMeal3", "VegetarianMeal4"]
}
}
extension Customer where Self:Paleo {
func getMeals() -> Array<String> {
return ["PaleoMeal1", "PaleoMeal2", "PaleoMeal3", "PaleoMeal4"]
}
}
and this struct
struct aCustomer:Customer, Vegan {
var name:String
var age: Int
var startDate:Date
var meals:Array<String>
var market:Int
}
when I create a new object based on that struct
var newCustomer = aCustomer(name:"William", age:40, startDate:Date(), meals:[], market:1)
how come I can't access the getMeals function in the extensions? I get an error stating getMeals is an ambiguous reference.
extension Customer where Self:Vegan, Self:Vegetarian {
extends Customer in the case where the adopter or Customer also both adopts Vegan and Vegetarian. Your aCustomer (a struct type starting with a small letter?? the horror, the horror) does not do that, so the extension doesn't apply to it.
If the goal is to inject the same code either when the adopter adopts Vegan or when the adopter adopts Vegetarian, use two extensions, or, if you don't like the repetition, have them both adopt some "higher" protocol that is extended with the desired code to be injected.

specialized extension of Dictionary

How can I declare an extension that will work only for a particular type?
I tried this:
extension Dictionary where
Key : CustomStringConvertible,
Value: CustomStringConvertible
{
func queryString() -> String {
var paramArray = Array<String>()
for (key, value) in self {
paramArray.append("\(key.description)=\(value.description)")
}
return "&".join(paramArray)
}
}
And it compiles fine. But when I try to use it
var d = Dictionary<String, String>()
var q = d.queryString() // <-- ERROR
I get the error:
Cannot invoke 'queryString' with no arguments
What is wrong here? I want to be able to call queryString on a Dictionary but only when it is Dictionary<String, String>
Any help is highly appreciated.
Edit
As #jtbandes said, String does not conform to CustomStringConvertible. CustomStringConvertible Protocol Reference suggests to use String() constructor to get a string rather than using the protocol as a constrain.
NOTE: String(instance) will work for an instance of any type, returning its description if the instance happens to be CustomStringConvertible. Using CustomStringConvertible as a generic constraint, or accessing a conforming type's description directly, is therefore discouraged.
extension Dictionary {
public func queryString() -> String {
var paramArray = Array<String>()
for (key, value) in self {
paramArray.append("\(String(key))=\(String(value))")
}
return "&".join(paramArray)
}
}
Edit2
This is my final version.
extension Dictionary {
public func queryString() -> String {
var queryItems = Array<NSURLQueryItem>()
for (key, value) in self {
queryItems.append(NSURLQueryItem(name: String(key), value: String(value)))
}
let comps = NSURLComponents();
comps.queryItems = queryItems
return comps.percentEncodedQuery!
}
}
String is not CustomStringConvertible. You can use:
extension String: CustomStringConvertible {
public var description: String { return self }
}
Or, I would recommend making your own protocol for this case:
protocol QueryStringConvertible {
var queryStringRepresentation: String { get }
}
extension String: QueryStringConvertible {
var queryStringRepresentation: String { return self /*probably should escape the string here*/ }
}
extension Dictionary where
Key : QueryStringConvertible,
Value : QueryStringConvertible ...
But in reality, I think you might want to take a look at NSURLQueryItem and NSURLComponents :)