just curious about something.
private enum CodingKeys: String, CodingKey {
case id = "page"
}
If I were to place String after the CodingKey protocol I would get an error saying I need to place String before.. well what determines the priority and why does that even matter? Thanks in advance.
Protocol conformance order does not matter. In your case you are getting an error when you place String after CodingKey is because String is not a protocol. It is a struct. Look at the error message what you get in Xcode:
Raw type 'String' must appear first in the enum inheritance clause
By placing String after the enum declaration will define its raw value.
To prove the point let's define two protocols:
protocol Protocol1 { }
protocol Protocol2 { }
And try to change the order they conform to your CodingKeys enum. This would be valid:
private enum CodingKeys: String, CodingKey, Protocol1, Protocol2 {
case id = "page"
}
And this is valid too:
private enum CodingKeys: String, CodingKey, Protocol2, Protocol1 {
case id = "page"
}
It’s a language requirement that for a declaration of Enumerations with Cases of a Raw-Value Type the raw-value type must come before any adopted protocol.
See https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID364
Related
I'm trying to write a function that accepts any value that is RawRepresentable by a CustomStringConvertible. I tried writing this:
enum MyEnum: String {
case a = "someString"
}
func myFunction<R: RawRepresentable>(val: R) where R.RawValue == CustomStringConvertible {
print(val.rawValue.description)
}
myFunction(val: MyEnum.a)
However I get the following error:
Global function 'myFunction(val:)' requires the types 'String' and 'CustomStringConvertible' be equivalent
Which is weird, since String does conform to CustomStringConvertible.
Conforming the RawValue to just String works, however, I'd like to make this work with other CustomStringConvertible.
Why does this not compile, and is there a way I can achieve that?
You should say that it conforms the protocol
where R.RawValue: CustomStringConvertible
Now it works also for other types
enum MyEnum2: Int {
case one = 1
}
myFunction(val: MyEnum2.one)
I came across this brilliant compile-time error and I'm trying to figure out how it works.
Here it is, boiled down to its most basic form.
struct Object: Codable {
let a: Int
let b: Int
enum CodingKeys: String, CodingKey {
case a
case b
}
}
The above snippet compiles just fine, as expected. However, the following snippet fails because it does not conform to the type Decodable:
struct Object: Codable {
let a: Int
let b: Int
enum CodingKeys: String, CodingKey {
case a
}
}
My assumption is that Swift somehow expects the enum CodingKeys to contain a case for each property defined on Object.
Is this assumption correct, and if so, how does Swift know that?
The following code (compiles without errors) retrieves index of an element
in a particular CaseIterable enum type
public enum MyEnum : CaseIterable {
case ONE, TWO, THREE
public func ordinal() -> Int? {
return MyEnum.allCases.firstIndex(of: self)
}
}
I want to make a generic function to work with all CaseIterable enums.
If I try:
public extension CaseIterable {
public func ordinal() -> Int? {
return CaseIterable.allCases.firstIndex(of: self)
}
}
I get a compiler error "Member 'allCases' cannot be used on value of protocol type 'CaseIterable'; use a generic constraint instead" which is quite logical, as the actual enum type is unknown".
When I try CaseIterable<T>, I get another error, as CaseIterable is not declared as generic type.
Is there a way?
Couple of changes are necessary:
The return type needs to be Self.AllCases.Index? rather than Int?. In practice, these types will be equivalent, as seen below.
You also need to constrain any types to Equatable, because you need to be equatable in order to use firstIndex(of:). Again, in practice, any CaseIterable will usually be an enum without associated values, meaning it will be equatable automatically.
(Optional change) This function will never return nil, because you're finding one case in a CaseIterable. So you can remove the optionality on the return type (Self.AllCases.Index) and force unwrap.
Example:
public extension CaseIterable where Self: Equatable {
public func ordinal() -> Self.AllCases.Index {
return Self.allCases.firstIndex(of: self)!
}
}
enum Example: CaseIterable {
case x
case y
}
Example.y.ordinal() // 1
type(of: Example.y.ordinal()) // Int
Personally, I'd add that "Ordinal" usually means something different than what you're doing, and I'd recommend changing the function name to elementIndex() or something. But that's an aside.
With a CaseIterable CodingKeys enum and Mirror I can almost implement a runtime-type-checked version of the compiler-magic Encodable implementation. Almost, but not quite, because from a CodingKey value I can't get back to the enum case name, which I need to look up the matching value via Mirror(reflecting: myEncodable).
enum CodingKeys: String, CodingKey {
case defaultName // default: .stringValue is also the myEncodable member name
case nonDefaultName = "some-other-name" // .stringValue is not the myEncodable name, no access to "nonDefaultName"
}
I would have expected to be able to find the case name somehow via Mirror(reflecting: CodingKeys.nonDefaultName) but that doesn't seem to be the case.
Am I missing something? Is this in fact possible using Mirror? (I'm still on Xcode 9, by the way, faking CaseIterable until I get the real thing.)
Any alternative ways to get to the same goal (guarantee that all members mentioned in CodingKeys get looked up on the object and fed to the Encoder)?
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.