Is it possible to have a protocol that specializes a generic protocol? I want something like this:
protocol Protocol: RawRepresentable {
typealias RawValue = Int
...
}
This does compile, but when I try to access the init or rawValue from a Protocol instance, its type is RawValue instead of Int.
In Swift 4, you can add constraints to your protocol:
protocol MyProtocol: RawRepresentable where RawValue == Int {
}
And now all methods defined on MyProtocol will have an Int rawValue. For example:
extension MyProtocol {
var asInt: Int {
return rawValue
}
}
enum Number: Int, MyProtocol {
case zero
case one
case two
}
print(Number.one.asInt)
// prints 1
Types that adopt RawRepresentable but whose RawValue is not Int can not adopt your constrained protocol:
enum Names: String {
case arthur
case barbara
case craig
}
// Compiler error
extension Names : MyProtocol { }
Related
I have a protocol MyProtocol which has an associatedtype, inferred by the return type of myFunction
struct MyStruct: Codable {
var myVar: Int
}
protocol MyProtocol {
associatedtype MyType
func myFunction()-> MyType
}
class MyClass1: MyProtocol {
func myFunction()-> MyStruct? {
return MyStruct(myVar: 1) //ex.
}
}
class MyClass2: MyProtocol {
func myFunction()-> Int? {
return 1 // ex.
}
}
In MyClass1, associatedtype is infered as MyStruct?
In MyClass2, associatedtype is infered as Int?
from there everything works fine. No I want to build another protocol that can only be applied to a MyProtocol if associatedtype is Codable :
protocol MyProtocolCodable where Self: MyProtocol, MyType == Codable? {}
the code runs fine until here but when I try to apply it to my class I get an error:
extension MyClass1: MyProtocolCodable{} 🛑
'MyProtocolCodable' requires the types 'MyStruct?' and 'Codable?' (aka 'Optional<Decodable & Encodable>') be equivalent
yet, as far as I can see MyStruct? (aka Optional) and Codable? (aka Optional<Decodable & Encodable>) are equivalent?
How can I get rid of this error message? Am I doing something that is not meant to be done?
As far as I can see MyStruct? (aka Optional) and Codable? (aka
Optional<Decodable & Encodable>) are equivalent?
No, they are not. MyStruct conforms to Codable but is not equivalent of it.
As in: every MyStruct is Codable but not every Codable is MyStruct.
You can try changing MyType == Codable? to MyType: Codable:
protocol MyProtocolCodable where Self: MyProtocol, MyType: Codable {}
as MyStruct is not equal to Codable?.
I need to create generic function in protocol with default implementation in extension. It func should work with item as enum:RawRepresentable where RawValue == String always. I tried
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable // how this add restriction to RawValue == String
func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String
}
enum RequiresEnumDefault: String {
case `default`
}
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String {
typealias SectionIdentifierEnum = RequiresEnumDefault
func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String {
print(T.rawValue)
}
}
but i have errors
'SectionIdentifierEnum' is ambiguous for type lookup in this context
'RawValue' is not a member type of 'T'
Any solutions
Generally when covering generics in the context of protocols, the generic typeholder is seen as representable by an associatedtype of the protocol. In your example this would be SectionIdentifierEnum, which acts as a placeholder for a constrained type. SectionIdenfierEnum is not, however, a protocol by itself, so cannot use it as a type constraint in a generic method. You can, however, use it as the type itself in your test(...) method.
Swift 3.1
Now, currently (Swift 3.1), you can't add sophisticated type constrains to an associatedtype. You could can, however, supply a default implementation available only for the case the where Self derives from UIViewController and implements the RequiresEnum protocol by setting the SectionIdentifierEnum type to the concrete RequiresEnumDefault type. The latter will ascertain that the associated RawValue is String for this default implementation, as the RawValue of the concrete RequiresEnumDefault type is String.
E.g.:
// Swift 3.1
// ---------
// Types that implement this protocol mustn't necessarily use a
// `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue`
// is constrained to equal `String`.
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable
func test(identifier: SectionIdentifierEnum)
}
enum RequiresEnumDefault: String {
case `default`
}
// This extension, however, is only available for types that use
// `RequiresEnumDefault ` as the concrete type of `SectionIdentifierEnum`
// (in which case `SectionIdentifierEnum.RawValue` is `String`).
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault {
func test(identifier: SectionIdentifierEnum) {
print(identifier.rawValue)
}
}
// Example usage.
class MyViewController : UIViewController, RequiresEnum {
typealias SectionIdentifierEnum = RequiresEnumDefault
// ...
}
let foo = MyViewController()
foo.test(identifier: RequiresEnumDefault.default)
// prints "default" (using extension:s default implementation)
Above, the default implementation of test(...) is only available when SectionIdentifierEnum equals the concrete type RequireEnumDefault (and Self derives from UIViewController ...). If instead you want it to only be available when SectionIdentifierEnum is any enum with String typed RawValue, you modify the type constraint of the extensions accordingly:
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable
func test(identifier: SectionIdentifierEnum)
}
enum RequiresEnumDefault: String {
case `default`
}
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String {
func test(identifier: SectionIdentifierEnum) {
print(identifier.rawValue)
}
}
// Example usage.
enum EnumWithStringRawValue: String {
case foo
}
class MyViewController : UIViewController, RequiresEnum {
typealias SectionIdentifierEnum = EnumWithStringRawValue
// ...
}
let foo = MyViewController()
foo.test(identifier: EnumWithStringRawValue.foo)
// prints "foo" (using extension:s default implementation)
Once Swift 4 is released, you'll be able to add more sophisticated constraints to associatedtype:s, as per the implementation of Swift evolution proposal:
SE-0142: Permit where clauses to constrain associated types
In which case the above can be modified into:
// Swift 4
// -------
// Here, all types that implement this protocol must use a
// `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue`
// is equal to `String`.
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable
where SectionIdentifierEnum.RawValue == String
func test(identifier: SectionIdentifierEnum)
}
enum RequiresEnumDefault: String {
case `default`
}
// For the specific case where `SectionIdentifierEnum` equals
// `RequiresEnumDefault` (and where `Self` derives from `UIViewController`),
// this default implementation is readily available.
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault {
func test(identifier: SectionIdentifierEnum) {
print(identifier.rawValue)
}
}
// Example usage.
class MyViewController : UIViewController, RequiresEnum {
typealias SectionIdentifierEnum = RequiresEnumDefault
// ...
}
let foo = MyViewController()
foo.test(identifier: RequiresEnumDefault.default)
// prints "default" (using extension:s default implementation)
Likewise modifying the constraint on the default implementation of test(...) not only to the case where SectionIdentifierEnum equals RequiresEnumDefault (but to any enum: in this case we know such enum will always have a RawValue String, due to the constraint on the associatedtype in the protocol definition).
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable
where SectionIdentifierEnum.RawValue == String
func test(identifier: SectionIdentifierEnum)
}
enum RequiresEnumDefault: String {
case `default`
}
// From the constraint on the `RawValue` of `SectionIdentifierEnum`
// above, we know that `RawValue` equals `String`.
extension RequiresEnum where Self: UIViewController {
func test(identifier: SectionIdentifierEnum) {
print(identifier.rawValue)
}
}
// Example usage.
enum EnumWithStringRawValue: String {
case foo
}
class MyViewController : UIViewController, RequiresEnum {
typealias SectionIdentifierEnum = EnumWithStringRawValue
// ...
}
let foo = MyViewController()
foo.test(identifier: EnumWithStringRawValue.foo)
// prints "foo" (using extension:s default implementation)
The following contrived Swift 2 example from real-world code won't compile:
protocol SomeModelType { }
protocol SomeProtocol {
var someVar: SomeModelType? { get }
}
class ConcreteClass<T: SomeModelType>: SomeProtocol {
var someVar: T?
}
This doesn't make sense to me fully. I would assume in ConcreteClass that because I have T being constrained to SomeModelType and have T as the backing type for the someVar property, the compiler would be able to figure out that the SomeProtocol was being conformed to by ConcreteClass.
How should an example like this be structured? Is it possible to the Swift compiler to determine protocol conformance through generic type constraints?
protocol SomeModelType { }
protocol SomeProtocol {
associatedtype T: Any
var someVar: T? { get }
}
class ConcreteClass<T> :SomeProtocol where T: SomeModelType {
var someVar: T?
}
If I want to add an extension to every object I can do the following:
extension AnyObject
{
func myFunc() { ... }
}
Is there something similar where I can add a function to every Enum? In other words, what is the base "class" for every enum?
First of all, note that you cannot do an extension to AnyObject as above, as AnyObject is a protected protocol (to which all classes implicitly conform) rather than a type. See e.g.
Is there a way to add an extension to AnyObject?
Now, you could, however, extend any specific type as you show above, e.g., extension Int { ... }. However enum is not a type; rather a "container" of ordered raw representable values. So a more valid comparison could be "If I want to add an extension to every class, by extension class ...", which is, naturally, trickier.
Now, all enumerations with a rawValue and an initializer by rawValue conforms to protocol RawRepresentable, so you could extend this protocol for specific types of raw values:
extension RawRepresentable where RawValue == Int {
var sqrt: Double {
return pow(Double(rawValue),(1/2))
}
}
And this extension property would be available to all enumerations that explicitly use the same raw value type, in this case, Int:
enum MyIntegerLiteralEnum : Int {
case One = 1
case Two
case Three
case Four
}
print(MyIntegerLiteralEnum.One.sqrt)
print(MyIntegerLiteralEnum.Two.sqrt)
print(MyIntegerLiteralEnum.Four.sqrt)
/* 1.0
1.4142135623731
2.0 */
As a disclaimer, note that this extension will be made available to all types that conforms to RawRepresentable with a rawValue of type Int, not only enum types. E.g.:
struct Foo : RawRepresentable {
typealias RawValue = Int
var bar : Int
var rawValue: RawValue {
get {
return bar
}
}
init(rawValue bar: Int) {
self.bar = bar
}
}
var a = Foo(rawValue: 16)
print(a.sqrt) // 4.0
Would it be possible to have a function that allows any enum where the rawValue is of a certain type? For example, any enum that has a string rawValue.
This can be done using generics and the "where" keyword
enum EnumString: String {
case A = "test"
}
func printEnum<T: RawRepresentable where T.RawValue == String>(arg: T) {
print(arg.rawValue)
}
printEnum(EnumString.A) //Prints "test"
You can declare a generic that conforms to the type RawRepresentable, which is a protocol that all enums declaring a primitive rawValue conform to.
enum EnumA: Int {
case A = 0
}
enum EnumB {
case A
}
func doGenericSomething<T: RawRepresentable>(arg: T) {
println(arg.rawValue)
}
doGenericSomething(EnumA.A) //OK
doGenericSomething(EnumB.A) //Error! Does not conform to protocol RawRepresentable
You cannot, however, specify an enum's rawValue type in a generic. For information you can see the post here.