Simplified example
Take a look at this simple protocol
protocol FooOwner {
static var foo: Self { get }
}
I would like an enum to conform to said protocol, and in my opinion this ought to work, since the static syntax SomeTypeConformingToFooOwner.foo should result in an instance of SomeTypeConformingToFooOwner and in the case where SomeTypeConformingToFooOwner is a enum.
enum Foo: FooOwner { // Type 'Foo' does not conform to protocol FooOwner
case foo
}
The workaround is this ugly thing:
protocol FooOwner {
static var fooOwner: Self { get }
}
enum Foo: FooOwner {
case foo
static var fooOwner: Foo {
return Foo.foo
}
}
Do you have a nicer workaround for enum conforming to protocols with static vars?
Real use case
protocol StringConvertibleError: Swift.Error {
static var invalidCharactersError: Self { get }
}
protocol StringConvertibleErrorOwner {
associatedtype Error: StringConvertibleError
}
protocol StringConvertible {
var value: String { get }
/// Calling this with an invalid String will result in runtime crash.
init(validated: String)
init(string value: String) throws
static func validate(_ string: String) throws -> String
}
// MARK: - Default Implementation Constrained
extension StringConvertible where Self: CharacterSetSpecifying, Self: StringConvertibleErrorOwner {
static func validate(_ string: String) throws -> String {
guard Self.allowedCharacters.isSuperset(of: CharacterSet(charactersIn: string)) else {
throw Error.invalidCharactersError
}
// Valid
return string
}
}
struct HexString: StringConvertible, CharacterSetSpecifying, StringConvertibleErrorOwner {
static var allowedCharacters = CharacterSet.hexadecimal
let value: String
init(validated unvalidated: String) {
do {
self.value = try HexString.validate(unvalidated)
} catch {
fatalError("Passed unvalid string, error: \(error)")
}
}
}
extension HexString {
enum Error: StringConvertibleError {
static var invalidCharactersError: Error {
return Error.invalidCharacters
}
case invalidCharacters
}
}
So it's the last part that I would like to change to:
extension HexString {
enum Error: StringConvertibleError {
case invalidCharacters
}
}
Since I have many similar types to HexString.
Yes of course obviously I can use one shared Error enum, but I would like to have one specific enum per type.
SE-0280 solves this - if accepted. It is currently in review.
Edit 1
Great news, SE-0280 has been accepted.
This might not fully match OP's issue, but as it seems somewhat related, I'm throwing it out there in case it helps.
In my case, I cared about some cases, but other cases could be ignored (or handled in a common way).
I admit my approach is not elegant and requires a lot of boiler plate, but it got me past a similar problem.
// Given these enums, we want a protocol that matches enums with
// 'foo' and 'bar' cases.
enum FooFriends {
case foo // conforms
case bar // conforms
case baz // don't really care
}
enum FooBar {
case foo // conforms
case bar // conforms
}
// We wish we could do this:
protocol FooBarWish {
case foo // or static var foo: Self { get }
case bar // or static var bar: Self { get }
}
// Workaround
// the boiler plate
enum FooBarProxy {
case foo
case bar
case other
}
protocol FooBarProtocol {
func getProxy() -> FooBarProxy
}
extension FooFriends: FooBarProtocol {
func getProxy() -> FooBarProxy {
switch self {
case .foo:
return .foo
case .bar:
return .bar
default:
return .other
}
}
}
extension FooBar: FooBarProtocol {
func getProxy() -> FooBarProxy {
switch self {
case .foo:
return .foo
case .bar:
return .bar
}
}
}
// Usage
// Instead of the ideal case (which won't work)
let fooBarOrFooFriend1: FooBarWish = FooFriends.foo
let fooBarOrFooFriend2: FooBarWish = FooBar.bar
// We can get by with
let fooBarProxy1 = FooFriends.foo.getProxy()
let fooBarProxy2 = FooBar.bar.getProxy()
// Verification
func checkIt(_ foobar: FooBarProxy) {
switch foobar {
case .foo:
print("it was foo")
case .bar:
print("it was bar")
case .other:
print("it was neither")
}
}
checkIt(fooBarProxy1)
checkIt(fooBarProxy2)
// =>
// it was foo
// it was bar
Related
I hoped that Swift gives me the ability to create an extension for type with specified conditions in where block. I imagined that I can extend the same generic type with different extensions dependent on concrete generic type value (T). But not. Following example demonstrates my problem:
protocol P {
associatedtype Prop
var property: Prop { get }
}
enum E<T: P> {
case single(T)
case double(T)
}
extension E: P where T.Prop == Int {
var property: Int {
switch self {
case .single(let o): return o.property
case .double(let o): return o.property * 2
}
}
}
extension E: P where T.Prop == String {
var property: String {
switch self {
case .single(let o): return o.property
case .double(let o): return o.property + o.property
}
}
}
struct Int4: P {
var property: Int {
return 4
}
}
struct StringHello: P {
var property: String {
return "Hello"
}
}
print(E.single(Int4()).property)
print(E.double(StringHello()).property)
Following error and note are the result of the compilation.
error: conflicting conformance of 'E' to protocol 'P'; there cannot be more than one conformance, even with different conditional bounds
extension E: P where T.Prop == String {
note: 'E' declares conformance to protocol 'P' here
extension E: P where T.Prop == Int {
Is it really impossible? Why? What can I do with my code to succeed?
Some details to demonstrate the problem in my real situation.
I have some generic enum, which is used with many different wrapped types.
enum Color<T> {
case red(T), green(T)
func map<T2>(_ transform: (T) -> T2) -> Color<T2> {
switch self {
case .red(let o): return .red(transform(o))
case .green(let o): return .green(transform(o))
}
}
}
Very often, I need different extensions for Color to conform it to different protocols depending on the wrapped type. Sometimes these protocols have the same base (super) protocol and as a result, I have the current problem. Sometimes I cant extend Color to conform this base (super) protocol for all deriving protocols because I need different implementations.
Is it impossible? Yes and no. It's not currently possible in Swift, as it has been implemented. It is in principle possible to be implemented.
The name for this is "overlapping conformances", and it was explicitly and purposely rejected. You can find the rationale in the "Alternatives considered" section of SE-0143 Conditional conformances. The TL;DR is: because it's really complicated.
Without knowing more about what exactly you were trying to use this for, there's not much direction we can provide.
As was described earlier you cannot just do this kind of extension. However, you can use hack like this:
protocol SomeExtension {
func doSomething()
}
extension SomeExtension {
func doSomething() {
print("Do nothing or error")
}
}
extension SomeExtension where Self == [String] {
func doSomething() {
print("String")
}
}
extension SomeExtension where Self == [Int] {
func doSomething() {
print("Int")
}
}
extension Array: SomeExtension { }
let stringsArr = ["a", "b", "d"]
let numbersArr = [1, 2, 3]
stringsArr.doSomething()
numbersArr.doSomething()
In console you can see
String
Int
I need some kind of enum which can accept any StringLiteralType in which I don't need to create a lot of boilerplate code.
Here's an example of the boilerplate code I have.
enum Sample: RawRepresentable {
case foo
case bar
case unknown(String)
init?(rawValue: String) {
if let correspondingValue = Key(rawValue: rawValue)?.correspondingValue {
self = correspondingValue
} else {
self = .unknown(rawValue)
}
}
private enum Key: String {
case foo
case bar
var correspondingValue: Sample {
switch self {
case .foo: return .foo
case .bar: return .bar
}
}
init?(withSample sample: Sample) {
switch sample {
case .foo: self = .foo
case .bar: self = .bar
case .unknown: return nil
}
}
}
var rawValue: String {
switch self {
case let .unknown(value): return value
default: return Key(withSample: self)?.rawValue ?? ""
}
}
}
I want to have defined cases (foo, bar, etc) which have default values that I can use to switch against, and then I want to have an unkown(String) which can contain any value.
This can be easily done by just using String and some kind of Constants like this for example.
enum Constants {
static let foo = "foo"
static let bar = "bar"
}
// sample usage
let someString = "aaaa"
let sample = Sample(rawValue: someString)! // don't mind the implicit unwrapping
switch sample {
case Constants.foo:
// do something
case Constants.bar:
// do something
default:
// do something with unknown someString
}
The idea here is to be able to use sample like this.
let someString = "aaaa"
let sample = Sample(rawValue: someString)! // don't mind the implicit unwrapping
switch sample {
case .foo:
// do something
case .bar:
// do something
case .unknown(let value):
// do something
}
EDIT:
Why it needs to be an Enum
- Autocomplete in XCode using enums
- Adding functionality will be easy when adding new cases when using switch
Why it needs to be RawRepresentable
- This gets stored to persistence via it's RawValue.
- I can also use protocols under ExpressibleByXXXXXXLiteral by doing this.
Does it need to be RawRepresentable? The code below works per your requirement…
enum Sample {
case foo, bar, unknown(StringLiteralType)
init(_ string: StringLiteralType) {
switch string {
case "foo": self = .foo
case "bar": self = .bar
default: self = .unknown(string)
}
}
}
let sample = Sample("aaa")
switch sample {
case .foo:
print("foo")
case .bar:
print("bar")
case .unknown(let value):
print(value)
}
// aaa
Edit
enum Sample: RawRepresentable {
case foo, bar, unknown(StringLiteralType)
enum Keys: String {
case foo, bar
var sample: Sample {
switch self {
case .foo: return .foo
case .bar: return .bar
}
}
}
init(rawValue: StringLiteralType) {
self = Keys(rawValue: rawValue)?.sample ?? .unknown(rawValue)
}
var rawValue: String {
switch self {
case .foo: return Keys.foo.rawValue
case .bar: return Keys.bar.rawValue
case .unknown(let value): return value
}
}
}
print(Sample(rawValue: "aaa").rawValue) // aaa
print(Sample(rawValue: "foo").rawValue) // foo
print(Sample.foo.rawValue) // foo
print(Sample.bar.rawValue) // bar
This is the best solution I made so far.
I first created an enum which contains two cases with associated values, one is for a sub enum of known entities and the other is for unknown entities.
enum Foo {
case known(Bar)
case unknown(String)
enum Bar: String {
case fiz, baz
}
}
I then extended the said enum to have RawRepresentable capabilities
extension Foo: RawRepresentable {
init?(rawValue: String) {
if let bar = Bar(rawValue: rawValue) {
self = .known(bar)
} else {
self = .unknown(rawValue)
}
}
init(stringValue: String) {
self.init(rawValue: stringValue)!
}
var rawValue: String {
switch self {
case let .known(bar): return bar.rawValue
case let .unknown(string): return string
}
}
}
Here's a sample usage.
let foo: Foo = .known(.fiz)
switch foo {
case .known(.fiz):
... do something
case .known(.baz):
... do something
case .unknown(let str):
... do something
}
PS: This could possibly be a nice thing to propose in Swift Evolution, so there would be less boilerplate I guess.
I would like to have a protocol that defines some methods and properties. However, property types and method return types may vary between the different classes that conform to said protocol. For example: A.getContent() may return a value of type String, but B.getContent() may return a value of type Int. In my example below, I used the type Any. Is this possible in Swift or is this a totally wrong approach? Maybe with generics?
protocol Content {
func getContent() -> any
}
class A: Content {
func getContent() -> String {
return "Im a String"
}
}
class B: Content {
func getContent() -> Int {
return 1234
}
}
I think you are looking about Generics in Protocol.
You can associate a type dynamically with associatedtype, for example
protocol Content{
associatedtype T
func getContent()-> T
}
class A: Content {
func getContent() -> String {
return "Hello World"
}
}
class B: Content {
func getContent() -> Int {
return 42
}
}
A().getContent() //"Hello World"
B().getContent() //42
If you look at this example when you put the Type after the function in the class sun of Content, the protocol Content will be this one type
Update
I am putting another example using "swiftly" syntax instead of traditional getContent.
protocol Content{
associatedtype T
var content:T { get }
}
class A: Content {
var content:String{
return "Hello World"
}
}
class B: Content {
var content:Int{
return 42
}
}
A().content //"Hello World"
B().content //42
You can use generics and meta-types:
protocol Content {
func getContent<T>(ofType: T.Type) -> T?
}
class A: Content {
func getContent<T>(ofType: T.Type) -> T? {
return "Im a String" as? T ?? nil
}
}
class B: Content {
func getContent<T>(ofType: T.Type) -> T? {
return 1234 as? T ?? nil
}
}
let aClass = A()
let aValue = aClass.getContent(ofType: String.self) // "Im a String"
let bClass = B()
let bValue = bClass.getContent(ofType: Int.self) // 1234
Is there any way to use array of protocol's generic?
For example,
/* I want to use protocol like below,
* but I can't because protocol is not concrete
* so cannot make array of it */
class MyClass<T where T:MyProtocol, T:[MyProtocol]> {
let result: T
}
protocol MyProtocol {
init(with: String)
}
class SpecialThing: MyProtocol {
let appleWatch: AppleWatch
init(with: String) {
self.appleWatch = AppleWatch(with)
}
}
class SampleClass {
func test {
typealias listCallback = (MyClass<[SpecialThing]>, NSError) -> ()
typealias oneCallback = (MyClass<SpecialThing>, NSError) -> ()
}
}
There can be one object or array of protocol's subclass.
I think "typealias" does not help me.
I want to find something more simple way.....
My first issue with this is that the type signature is wrong:
class MyClass<T where T:MyProtocol, T:[MyProtocol]>
That's the same type of thing as doing:
let t: String
let t: [String]
t = String("foo")
The compiler will complain because you are redefining T, once as a MyProtocol and again as an array of MyProtocol. You can't have both, you can only have one.
Answer: Use a construct like Either:
enum Either<T, U>
{
case Left(T)
case Right(U)
var leftValue: T?
{
if case .Left(let leftValue) = self
{
return leftValue
}
return nil
}
var rightValue: U?
{
if case .Right(let rightValue) = self
{
return rightValue
}
return nil
}
}
Allowing for:
class MyClass<T: MyProtocol>
{
let result: Either<T, [MyProtocol]>
}
I have tried to boil this issue down to its simplest form with the following.
Setup
Xcode Version 6.1.1 (6A2008a)
An enum defined in MyEnum.swift:
internal enum MyEnum: Int {
case Zero = 0, One, Two
}
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
and code that initializes the enum in another file, MyClass.swift:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
Error
Xcode gives me the following error when attempting to initialize MyEnum with its raw-value initializer:
Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
Notes
Per the Swift Language Guide:
If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration member or nil.
The custom initializer for MyEnum was defined in an extension to test whether the enum's raw-value initializer was being removed because of the following case from the Language Guide. However, it achieves the same error result.
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. [...]
If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.
Moving the enum definition to MyClass.swift resolves the error for bar but not for foo.
Removing the custom initializer resolves both errors.
One workaround is to include the following function in the enum definition and use it in place of the provided raw-value initializer. So it seems as if adding a custom initializer has a similar effect to marking the raw-value initializer private.
init?(raw: Int) {
self.init(rawValue: raw)
}
Explicitly declaring protocol conformance to RawRepresentable in MyClass.swift resolves the inline error for bar, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform to RawRepresentable).
extension MyEnum: RawRepresentable {}
Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?
This bug is solved in Xcode 7 and Swift 2
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
In your case this would result in the following extension:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}
You can even make the code simpler and useful without switch cases, this way you don't need to add more cases when you add a new type.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}
static let all: [VehicleType] = [car, moped, truck]
init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.
func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}
This works for Swift 4 on Xcode 9.2 together with my EnumSequence:
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun
var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}
init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}
for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("\(letter) for \(word)")
}
}
Output
A for Apple
C for Cat
F for Fun
Add this to your code:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}