In Swift, you can specify a union type using an enum. For example:
enum IntOrString {
case int(Int)
case string(String)
}
Now I want to write a property wrapper, which I can apply to properties of a type from my union type. However, I don't want to expose the enum I use behind the scenes. Instead, a user of my property wrapper should be able to apply it to a String
or Int
property directly.
One approach would work as follows.
@propertyWrapper
struct Managed<T> {
private var storage: IntOrString
var wrappedValue: T {
get {
switch storage {
case .int(let int):
return int as! T
case .string(let string):
return string as! T
}
}
set {
if T.self == Int.self {
storage = .int(newValue as! Int)
} else if T.self == String.self {
storage = .string(newValue as! String)
} else {
fatalError("Property has an illegal type")
}
}
}
}
Unfortunately however, I lose static type safety with this approach, as a user can now apply Managed
to a property which is neither Int
nor String
and would only get a runtime error, once he tries to set the property.
struct Foo {
@Managed var bar: Double
}
let foo = Foo(bar: 0) // fatal error
As far as I'm aware, I cannot specify a type constraint on T
which only allows for T
to be Int
or String
.
So is there any way in Swift to get static type safety on union types in this scenario?
If not, are there more elegant workarounds to my approach?
As far as I'm aware, I cannot specify a type constraint on
T
which only allows forT
to beInt
orString
.
Actually you can with a helper protocol
protocol OnlyStringOrInt {}
extension Int: OnlyStringOrInt {}
extension String: OnlyStringOrInt {}
With this solution the property wrapper can be reduced to
@propertyWrapper
struct Managed<T: OnlyStringOrInt> {
var wrappedValue: T
}
Then bar
declared as Double
raises this compiler error
Generic struct 'Managed' requires that 'Double' conform to 'OnlyStringOrInt'