I have a protocol. This is implemented by many structs that fall into one of two types of category: TypeOne and TypeTwo. I want to be able to distinguish between their types, so I've added an enum ProtocolType that defines the types typeOne and typeTwo. By default I set the protocolType to be typeOne, but I manually specify typeTwo when it's a TypeTwo struct:
enum ProtocolType {
case typeOne
case typeTwo
}
protocol MyProtocol {
let name: String { get }
var protocolType: ProtocolType { get }
}
extension MyProtocol {
var protocolType: ProtocolType {
return .typeOne
}
}
enum TypeOne {
struct Foo: MyProtocol {
let name = "foo"
}
}
enum TypeTwo {
struct Bar: MyProtocol {
let name = "bar"
let protocolType = .typeTwo
}
}
Is there any way I can remove the necessity for defining protocolType in all structs and somehow use generics to identify what type a struct is? They're already defined under the TypeOne and TypeTwo convenience enums, I was wondering if I could utilise that some how?
Given some protocol:
protocol MyProtocol {
var name: String { get }
}
It sounds like you want to "tag" certain types as special, even though they have the same requirements. That's not an enum, that's just another type (protocol):
// No additional requirements
protocol SpecialVersionOfMyProtocol: MyProtocol {}
You can then tag these at the type level, not the value level:
struct Foo: MyProtocol {
let name = "foo"
}
struct Bar: SpecialVersionOfMyProtocol {
let name = "bar"
}
And you can tell the difference using is if you need to:
func f<T: MyProtocol>(x: T) {
if x is SpecialVersionOfMyProtocol {
print("special one")
}
}
In most cases, though, I wouldn't use this kind of runtime check. I'd just have two protocols (one for TypeOne and one for TypeTwo), and implement whatever you need as extensions on those. For example, say you want to print the name differently depending on the type. Start with a protocol that just expresses that:
protocol NamePrintable {
var name: String { get }
func printName()
}
func printIt<T: NamePrintable>(x: T) {
x.printName()
}
Then extend that for TypeOnes and TypeTwos:
protocol TypeOne: NamePrintable {}
extension TypeOne {
func printName() {
print("I'm a type one with the name \(name)")
}
}
protocol TypeTwo: NamePrintable {}
extension TypeTwo {
func printName() {
print("I'm a type two with the name \(name)")
}
}
And conform your structs:
struct Foo: TypeOne {
let name = "foo"
}
struct Bar: TypeTwo {
let name = "bar"
}
printIt(x: Foo()) // I'm a type one with the name foo
printIt(x: Bar()) // I'm a type two with the name bar
If you want a default implementation, you can hang it on NamePrintable, but I kind of recommend not doing that for what you've described. I'd probably just have "type one" and "type two" explicitly.
extension NamePrintable {
func printName() {
print("BASE FUNCTIONALITY")
}
}
Related
How would one go about assigning an instance of a class which can have a generic type, but you don't know what type that is until run time?
For example.
We have a protocol and enums that conform to it like this:
protocol Stage: CaseIterable, Hashable {
var fooBarLength: Int { get }
}
enum FirstStage: String, Stage {
var fooBarLength: Int { 10 }
case section1
case section2
}
enum SecondStage: String, Stage {
var fooBarLength: Int { 10 }
case section1
case section2
case section3
}
Next we have some kind of controller that uses the protocol as a generic type... comme ça...
class FooBarController<StageType: Stage>: UIViewController {
private var stages: [StageType: Float] = [:]
}
Then used like this:
func fooBarScreen(boop: SomethingThatKnowsAboutTheStages) {
var fooBarController: FooBarController // <---- how do I define this????
if boop.someCondition() {
fooBarController = FooBarController<FirstStage>()
} else {
fooBarController = FooBarController<SecondStage>()
}
}
In Java / Kotlin I could just do this as it is above, how do I achieve the same thing in Swift?
Currently I get
"Reference to generic type 'FooBarController' requires arguments in <...>"
Secondary Question
Is there a more generic way than having to use that if-statement here? Ideally I would like the fooBarScreen method to not care about the generic type and just have SomethingThatKnowsAboutTheStages provide the type for me.
You can specify a protocol for providing Stage types like so:
protocol StageProvider {
associatedtype T: Stage
func getType() -> T.Type
}
Then make your SomethingThatKnowsAboutTheStages or any other one conform this protocol:
class SomethingThatKnowsAboutTheStages: StageProvider {
typealias T = SecondStage
func getType() -> T.Type {
SecondStage.self
}
}
Add an initializer for your FooBarController:
class FooBarController<StageType: Stage>: UIViewController {
convenience init(stage: StageType.Type) {
self.init()
}
}
And finally use all these:
func fooBarScreen<T: StageProvider>(boop: T) {
let controller = FooBarController(stage: boop.getType())
}
How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol
I have a simple struct to group some strings
struct MyStruct {
static let bar = "BAR"
static let foo = "FOO"
}
MyStruct.bar will return the string BAR
I want to add more properties to each item in the struct. Like:
MyStruct.bar.desc should return some custom description about the MyStruct item bar.
Can we extend the struct like an enum? Something like:
protocol MyProtocol {
var desc:String { get }
}
extension MyProtocol where <No idea> { //where Self: RawRepresentable in Enum-case
var desc: String {
return "Custom description" //Based on the struct item
}
}
So that, struct MyStruct:MyProtocol {.. should work.
Note: I have N number of structs like MyStruct. So, I don't want to implement the custom properties inside each struct individually.
Thanks
Yes you can create a protocol
protocol MyProtocol {}
With a default value for desc
extension MyProtocol {
var desc: String { "Custom description" /*Based on the struct item*/ }
}
And add it to your structs like
struct MyStruct: MyProtocol {}
See https://replit.com/#Dev1an/Swift-protocol-extension for a more advanced version
If you you want to be able to write MyStruct().<item>.desc I would suggest you take a look at CustomStringConvertible:
Types that conform to the CustomStringConvertible protocol can provide their own representation to be used when converting an instance to a string.
I'm not sure why bar is static since it seems you want a property so I added a baz property of type Baz:
struct MyStruct: MyProtocol, CustomStringConvertible { // or you can make `MyProtocol` inherit from CustomStringConvertible
struct Baz: CustomStringConvertible {
let qux = "QUX"
var description: String {
qux
}
}
static let bar = "BAR"
static let foo = "FOO"
let baz = Baz()
}
protocol MyProtocol {
associatedtype Item: CustomStringConvertible
associatedtype OtherItem: CustomStringConvertible
static var bar: Item { get }
var baz: OtherItem { get }
}
extension MyProtocol where Self: CustomStringConvertible {
var description: String {
return print(String(describing: baz)
}
}
print(String(describing: MyStruct.bar)) // prints "BAR"
print(String(describing: MyStruct()) // prints "QUX"
Consider the following:
protocol SomeProtocol: Equatable {}
// then, elsewhere...
var someValue: Any?
func setSomething<T>(_ value: T) where T: SomeProtocol {
someValue = value
}
func getSomething<T>() -> T? where T: SomeProtocol {
return someValue as? T
}
These functions work fine but essentially act like computed properties. Is there any way to implement something like the following?
var something<T>: T where T: SomeProtocol {
get { return someValue as? T }
set { someValue = newValue }
}
Thank you for reading. Apologies if this question has already been asked elsewhere, I have searched but sometimes my search fu is weak.
You need to define the computed property on a generic type, the computed property itself cannot define a generic type parameter.
struct Some<T:SomeProtocol> {
var someValue:Any
var something:T? {
get {
return someValue as? T
}
set {
someValue = newValue
}
}
}
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)