I'm dealing with enum and subclassing in Swift.
Each child bring its own new properties which have to be stored in an Enum. This enum is declared with some values in the mother class. I'd like to add some value to this enum. I can't find out how to do it, I tried this without result :
extension MotherClass {
enum Enumeration {
case NewProperty
}
}
The only way to add items to an enum is to add them directly to its declaration. You cannot add more items to an enum through inheritance or any other extension mechanism: the enum must be fully defined at the point of its declaration.
You can use a protocol to solve your problem
protocol MyProtocol {
func code() -> Int
}
then create multiple enums according to your requirement
enum MyEnum:Int, MyProtocol {
func code() -> Int {
return self.rawValue
}
case ok1 = 0
case other1
}
and another enum
enum MyEnum2:Int, MyProtocol {
func code() -> Int {
return self.rawValue
}
case ok2 = 10
case other2
}
Then use MyProtocol where ever you want to get enum
struct MyStruct {
var test1: MyProtocol
var test2: MyProtocol
}
class MyClass {
var abc = MyStruct(test1: MyEnum1.ok1, MyEnum2.ok2)
}
Related
I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"
I'm new to generics and I want to apply it for this logic.
So I have created an enum of user types. I have a userModel object that contains this user type information.
Now my UIViewController A will contain a user object and a tableView. Based on the user type, the number of cells and the cell design will also change.
Basically I don't want to clutter the entire logic into cell ForRowAtIndexPath and other such methods, which is why I was looking into generics.
This is my code
enum UserType : Int {
case None = 0, Musician, Listener
}
class User {
var userType : UserType = .None
}
class A<T:User>: UIViewController {
var selectedUser: T?
}
extension A where T.userType : UserType.Musician
{
func foo() { print("Musician") }
}
This gives me an error
Playground execution failed:
error: MyPlayground.playground:13:53: error: enum element 'Musician' is not a member type of 'UserType'
extension A where T.userType : UserType.Musician {
I would like to know why this error is comming and if my approach is wrong.
To create extension you must provide only information known at compile time. userType is property of User and hence it's runtime information. It can't be used to create extension.
But I think you still might benefit from protocols and generics. I'm not sure if it suits you, but anyway here's how it can be done.
You can declare protocols for each type of user like this
protocol UserProtocol {
var userType: UserType { get }
func doYourThing()
}
protocol MusicianProtocol: UserProtocol {}
protocol ListenerProtocol: UserProtocol {}
and create default implementation of UserProtocol
extension UserProtocol where Self: MusicianProtocol {
var userType: UserType { return .musician }
func doYourThing() {
print("Do-di-do!")
}
}
extension UserProtocol where Self: ListenerProtocol {
var userType: UserType { return .listener }
func doYourThing() {
print("Anything except vynil is garbage!")
}
}
Of course this way you will need separate class for each user
class Musician: MusicianProtocol {}
class Listener: ListenerProtocol {}
but generic handling is still possible by using UserProtocol
func letsPlay(_ users: UserProtocol...) {
for u in users {
u.doYourThing()
}
}
let m = Musician()
let l = Listener()
letsPlay(m, l)
I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
extension Test {
func returnSomething() -> T {
let valueToReturn = readValueFromPLISTthatCanReturnAStringOrDictionary() as T
return valueToReturn
}
}
So finally I have 2 clases, TestString and TestDictionary that both implements Test protocol and I want to indicate the T parameter and I want to use the default implementation. How I do this?
class TestString: Test {}
class TestDictionary: Test { }
class TestString: Test where Test.T = String or similar?
I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
No problem. Let's write that down.
enum StringOrDictionary {
case string(String)
case dictionary([String: String])
}
protocol Test {
func returnSomething() -> StringOrDictionary
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
Sounds good. I'll assume that readValueFromPLISTthatCanReturnAStringOrDictionary() actually returns Any, since that's what is returned by propertyList(from:).
extension Test {
func returnSomething() -> StringOrDictionary {
let value = readValueFromPLISTthatCanReturnAStringOrDictionary()
switch value {
case let string as String: return .string(string)
case let dictionary as [String: String]: return .dictionary(dictionary)
default: fatalError() // Or perhaps you'd like to do something else
}
}
}
It'd probably be nice to name your type something more meaningful than StringOrDictionary, but other than that, it should be pretty straightforward. Just make a type that means what you say. You want a type that means "OR" and that is an enum. (If you want a type that means "AND" that's a struct BTW.)
Regarding your answer, this isn't legal:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
The way to define your T is to implement the required method.
class RandomClass: Test {
func returnSomething() -> String {
return ""
}
}
If you wanted to share some common code, then you can attach that as an extension rather than a default implementation. You could write a returnString() method and call it from the RandomClass.returnSomething(). This is all very useful in some cases, but I definitely wouldn't use it in this case. You don't mean "returns any possible type (T)." You mean "returns one of two possible types" and that's an enum, not a generic.
Update: Apparently they've added a new feature that they've talked about but I thought wasn't in yet. You could now implement RandomClass this way:
class RandomClass: Test {
typealias T = String
}
(Which is a very nice new feature, even if it's not a good answer for this problem.)
Here's a solution to your immediate problem:
Create 2 subtypes of your protocol, each with a different definition of the associated type, and a different default implementation. You select which default implementation you'd like your classes to use by picking between the 2 sub types.
The next issue here is that [String: String] isn't Hashable. This is due to a lack of support for conditional conformances (e.g. the ability to express that a Dictionary is Hashable iff the keys and values are both Hashable), one of Swift's largest downfalls, IMO. You'll probably want to use the type erasing wrapper AnyHashable.
protocol ResultProvider {
associatedtype Result: Hashable
func getResult() -> Result
}
protocol StringResultProvider: ResultProvider {
typealias Result = String
}
extension StringResultProvider {
func getResult() -> String {
return "A string result"
}
}
protocol IntResultProvider: ResultProvider {
typealias Result = Int
}
extension IntResultProvider {
func getResult() -> Int {
return 123
}
}
class TestIntResult: IntResultProvider {}
class TestString: StringResultProvider {}
print(TestString().getResult())
print(TestIntResult().getResult())
// protocol DictionaryResultProvider: ResultProvider {
// typealias Result = [String: String]
// }
// extension DictionaryResultProvider {
// func getResult() -> [String: String] {
// return ["A dictionary": "result"]
// }
// }
// class TestDictionaryProvider: DictionaryResultProvider {}
You need to specify the typealias when you extend the class, like so:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
extension String: Test {
typealias T = Int
}
func def() -> Int {
return 6
}
extension Test {
func returnSomething() -> T {
return def() as! Self.T
}
}
"".returnSomething()
6
However, I couldn't find a way to do it without force casting.
The only working solution is made the generic in the function and specify the variable type when calling the function. I was wondering if i could specify the T type when i implement the protocol in the class, similar like this:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
But previous example just don't work, so an alternative could be this:
protocol Test {
func doSomething<T>() -> T
}
extension Test {
func doSomething<T>(key: String) -> T {
return returnDictOrStringFromPLIST(key: key) as! T
}
}
class TestString: Test {
func getValue() {
let bah: String = doSomething()
}
}
class TestDict: Test {
func getValue() {
let bah: [String: String] = doSomething()
}
}
I have 2 class which have same number of properties with same name. I want to access property without type casting.
class A : NSObject {
var amount : Int = 10
}
class B : NSObject {
var amount : Int = 20
}
Now I want to double the value of amount property like this
main() {
let classA : A()
print(doubleValue(classA))
let classB : B()
print(doubleValue(classB))
}
func doubleValue(myClass:AnyObject) -> Int {
return myClass.amount * 2
}
Please suggest how can I achieve this.
This is exactly what protocol are used for. Let us call this new protocol Amountable and add the amount property.
protocol Amountable {
var amount: Int { get set }
}
If you want to provide a default implementation for doubleValue() you can event use protocol extension as follows:
extension Amountable {
mutating func doubleValue() {
self.amount *= 2
}
}
Finally, let your classes conform to the protocol:
class ClassA: Amountable {
// Implementation of classA here
}
class ClassB: Amountable {
// Implementation of classB here
}
objectA = ClassA()
objectA.doubleValue()
I have a series of several structs conforming to MyProtocol. I need an array of these structs' types (because they have a static method declared in MyProtocol that I need to be able to access). I have tried all kinds of things but I can't make Xcode like it.
Also, before this is marked as dupe – I tried this, but all I got was:
//Foo and Bar are structs conforming to MyProtocol
let MyStructArray: Array<MyProtocol.self> = [Foo.self, Bar.self]
//Protocol 'MyProtocol' can only be used as a generic constant because it has Self or associated type requirements
How about this?:
protocol MyProtocol {
static func hello()
}
struct Foo: MyProtocol {
static func hello() {
println("I am a Foo")
}
var a: Int
}
struct Bar: MyProtocol {
static func hello() {
println("I am a Bar")
}
var b: Double
}
struct Baz: MyProtocol {
static func hello() {
println("I am a Baz")
}
var b: Double
}
let mystructarray: Array<MyProtocol.Type> = [Foo.self, Bar.self, Baz.self]
(mystructarray[0] as? Foo.Type)?.hello() // prints "I am a Foo"
for v in mystructarray {
switch(v) {
case let a as Foo.Type:
a.hello()
case let a as Bar.Type:
a.hello()
default:
println("I am something else")
}
}
// The above prints:
I am a Foo
I am a Bar
I am something else
I found the problem. My protocol was inheriting from RawOptionSetType. Not sure why that caused an issue, but commenting that inheritance out made it work. Weird.