I have a protocol with an optional property.
Most of the types that conform to this protocol will have a matching optional property. However, one has a non-optional property of the same type and name.
protocol SomeProtocol {
var foo: Int? { get }
}
struct StructA: SomeProtocol {
let foo: Int?
}
struct StructB: SomeProtocol {
let foo: Int // Type 'StructB' does not conform to protocol 'SomeProtocol'
}
Pressing Xcode's "Fix - Do you want to add protocol stubs?" button adds the optional version of the property, but the structure now has invalid duplicated variable names:
struct StructB: SomeProtocol {
let foo: Int
var foo: Int? { return foo } // Invalid redeclaration of 'foo'
}
In the { get }-only case, I had assumed that this would "just work" due to the non-optional always satisfying the constraints of the optional, similar to how you can return a non-optional in a function with an optional return type. But apparently that is not the case.
This works the same for functions as well; a protocol's func bar() -> Int? is not satisfied by a conforming type declaring func bar() -> Int.
Is there any way around this issue? I would prefer not to rename the variables or add intermediate getters.
Has this situation been considered for Swift? What is the rational for not allowing a non-optional to satisfy an optional protocol variable?
If the protocol provides a default implementation that returns an optional:
protocol SomeProtocol {
var foo: Int? { get }
}
extension SomeProtocol {
var foo: Int? { return nil }
}
protocol-conforming types can then provide an overriding non-optional version of the variable/function:
struct StructB: SomeProtocol {
let foo: Int
}
I found this discussed on the Swift Evolution forum:
At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.
https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448
This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:
Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns
which is tracked on Stack Overflow here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
The extension-with-default-implementation solution offered by pkamb is helpful in terms of getting the compiler to recognize conformance, but you need to be aware of the strange behavior this can produce.
An object of the newly conforming type will return a different value depending on what type you cast it to (this also includes passing as to a parameter whose type is the protocol):
let bar = StructB(foo: 7)
let baz: SomeProtocol = bar
bar.foo // evaluates to 7
baz.foo // evaluates to nil (surprise!)
As someone recently commented on a related Swift bug ticket: "This can be quite surprising and perhaps could be considered a bug of its own?"
It definitely tripped me up.
Related
I have a class with 1 optional field and 1 non-optional field, both of them with Type AnotherClass and also conform CustomProtocol:
protocol CustomProtocol {}
class CustomClass: CustomProtocol {
var nonoptionalField: AnotherClass = AnotherClass()
var optionalField: AnotherClass?
}
class AnotherClass: CustomProtocol {
}
The field nonoptionalField is type AnotherClass and conforms CustomProtocol.
On the other hand, optionalField is actually Optional< AnotherClass> and therefore DOES NOT conform CustomProtocol:
for field in Mirror(reflecting: CustomClass()).children {
let fieldMirror = Mirror(reflecting: field.value)
if fieldMirror.subjectType is CustomProtocol.Type {
print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol")
} else {
print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol")
}
}
// nonoptionalField is AnotherClass and conforms CustomProtocol
// optionalField is Optional<AnotherClass> and DOES NOT conform CustomProtocol
How can I unwrap the Type (not the value) of optionalField property, so that I can associate it with its protocol CustomProtocol?
In other words, how can I get the wrapped Type AnotherClass from Optional< AnotherClass> Type?
LIMITATION:
I really have to use Swift reflection through Mirror and unfortunately the property .subjectType doesn't allow to unwrap the optional wrapped Type of Optional< AnotherClass> so far.
I do not believe there's a simple way to do this, given that we currently cannot talk in terms of generic types without their placeholders – therefore we cannot simply cast to Optional.Type.
Nor can we cast to Optional<Any>.Type, because the compiler doesn't provide the same kinds of automatic conversions for metatype values that it provides for instances (e.g An Optional<Int> is convertible to an Optional<Any>, but an Optional<Int>.Type is not convertible to a Optional<Any>.Type).
However one solution, albeit a somewhat hacky one, would be to define a 'dummy protocol' to represent an 'any Optional instance', regardless of the Wrapped type. We can then have this protocol define a wrappedType requirement in order to get the Wrapped metatype value for the given Optional type.
For example:
protocol OptionalProtocol {
// the metatype value for the wrapped type.
static var wrappedType: Any.Type { get }
}
extension Optional : OptionalProtocol {
static var wrappedType: Any.Type { return Wrapped.self }
}
Now if fieldMirror.subjectType is an Optional<Wrapped>.Type, we can cast it to OptionalProtocol.Type, and from there get the wrappedType metatype value. This then lets us check for CustomProtocol conformance.
for field in Mirror(reflecting: CustomClass()).children {
let fieldMirror = Mirror(reflecting: field.value)
// if fieldMirror.subjectType returns an optional metatype value
// (i.e an Optional<Wrapped>.Type), we can cast to OptionalProtocol.Type,
// and then get the Wrapped type, otherwise default to fieldMirror.subjectType
let wrappedType = (fieldMirror.subjectType as? OptionalProtocol.Type)?.wrappedType
?? fieldMirror.subjectType
// check for CustomProtocol conformance.
if wrappedType is CustomProtocol.Type {
print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol")
} else {
print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol")
}
}
// nonoptionalField is AnotherClass and conforms CustomProtocol
// optionalField is Optional<AnotherClass> and conforms CustomProtocol
This only deals with a single level of optional nesting, but could easily be adapted to apply to an arbitrary optional nesting level through simply repeatedly attempting to cast the resultant metatype value to OptionalProtocol.Type and getting the wrappedType, and then checking for CustomProtocol conformance.
class CustomClass : CustomProtocol {
var nonoptionalField: AnotherClass = AnotherClass()
var optionalField: AnotherClass??
var str: String = ""
}
/// If `type` is an `Optional<T>` metatype, returns the metatype for `T`
/// (repeating the unwrapping if `T` is an `Optional`), along with the number of
/// times an unwrap was performed. Otherwise just `type` will be returned.
func seeThroughOptionalType(
_ type: Any.Type
) -> (wrappedType: Any.Type, layerCount: Int) {
var type = type
var layerCount = 0
while let optionalType = type as? OptionalProtocol.Type {
type = optionalType.wrappedType
layerCount += 1
}
return (type, layerCount)
}
for field in Mirror(reflecting: CustomClass()).children {
let fieldMirror = Mirror(reflecting: field.value)
let (wrappedType, _) = seeThroughOptionalType(fieldMirror.subjectType)
if wrappedType is CustomProtocol.Type {
print("\(field.label!) is \(fieldMirror.subjectType) and conforms CustomProtocol")
} else {
print("\(field.label!) is \(fieldMirror.subjectType) and DOES NOT conform CustomProtocol")
}
}
// nonoptionalField is AnotherClass and conforms CustomProtocol
// optionalField is Optional<Optional<AnotherClass>> and conforms CustomProtocol
// str is String and DOES NOT conform CustomProtocol
This is an interesting question, but after fiddling around with it for a while, I previously believed (and was corrected wrong) that this could not be solved using native Swift, which, however, has been shown possibly by #Hamish:s answer.
The goal
We want access, conditionally at runtime, the Wrapped type (Optional<Wrapped>) of an instance wrapped in Any, without actually knowing Wrapped, only knowing that Wrapped possibly conforms to some protocol; in your example CustomProtocol.
The (not insurmountable) obstacles
There are a few obstacles hindering us in reaching a solution to this introspection problem, namely to test, at runtime, whether an instance of Optional<Wrapped> wrapped, in itself, in an instance of Any, holds a type Wrapped that conforms to a given protocol (where Wrapped is not known). Specifically, hindering us from a general solution that is viable even for the case where the value being introspected upon happens to be Optional<Wrapped>.none.
The first problem, as already noted in your question, is that optionals wrapped in Any instances are not covariant (optionals themselves are covariant, but that is in special case present also for e.g. some collections, whereas for custom wrapping types the default behaviour of non-covariance holds). Hence, we cannot successfully test conformance of the type wrapped in Any at its optional level, vs Optional<MyProtocol>, even if Wrapped itself conforms to MyProtocol.
protocol Dummy {}
extension Int : Dummy {}
let foo: Int? = nil
let bar = foo as Any
if type(of: bar) is Optional<Int>.Type {
// OK, we enter here, but here we've assumed that we actually
// know the type of 'Wrapped' (Int) at compile time!
}
if type(of: bar) is Optional<Dummy>.Type {
// fails to enter as optionals wrapped in 'Any' are not covariant ...
}
The second problem is somewhat overlapping: we may not cast an Any instance containing an optional directly to the optional type, or (by noncovariance) to an optional type of a protocol to which the wrapped type conforms. E.g.:
let foo: Int? = 1
let bar = foo as Any
let baz = bar as? Optional<Int>
// error: cannot downcast from 'Any' to a more optional type 'Optional<Int>'
let dummy = bar as? Optional<Dummy>
// error: cannot downcast from 'Any' to a more optional type 'Optional<Dummy>'
Now, we can circumvent this using a value-binding pattern:
protocol Dummy {}
extension Int : Dummy {}
let foo: Int? = 1
let bar = foo as Any
if case Optional<Any>.some(let baz) = bar {
// ok, this is great, 'baz' is now a concrete 'Wrapped' instance,
// in turn wrapped in 'Any': but fo this case, we can test if
// 'baz' conforms to dummy!
print(baz) // 1
print(baz is Dummy) // true <--- this would be the OP's end goal
}
// ... but what if 'bar' is wrapping Optional<Int>.none ?
But this is only a workaround that helps in case foo above is non-nil, whereas if foo is nil, we have no binded instance upon which we may perform type & protocol conformance analysis.
protocol Dummy {}
extension Int : Dummy {}
let foo: Int? = nil
let bar = foo as Any
if case Optional<Any>.none = bar {
// ok, so we know that bar indeed wraps an optional,
// and that this optional happens to be 'nil', but
// we have no way of telling the compiler to work further
// with the actual 'Wrapped' type, as we have no concrete
// 'Wrapped' value to bind to an instance.
}
I'm been playing around with a few different approaches, but in the end I come back to the issue that for an optional nil-valued instance wrapped in Any, accessing Wrapped (without knowing it: e.g. as a metatype) seems non-possible. As shown in #Hamish:s answer, however, this is indeed not insurmountable, and can be solved by adding an additional protocol layer above Optional.
I'll leave my not-quite-the-finish-line attempts above, however, as the techniques and discussion may be instructive for readers of this thread, even if they didn't manage to solve the problem.
In the following code, I want to test if x is a SpecialController. If it is, I want to get the currentValue as a SpecialValue. How do you do this? If not with a cast, then some other technique.
The last line there won't compile. There error is: Protocol "SpecialController" can only be used as a generic constraint because it has Self or associated type requirements.
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
Unfortunately, Swift doesn't currently support the use of protocols with associated types as actual types. This however is technically possible for the compiler to do; and it may well be implemented in a future version of the language.
A simple solution in your case is to define a 'shadow protocol' that SpecialController derives from, and allows you to access currentValue through a protocol requirement that type erases it:
// This assumes SpecialValue doesn't have associated types – if it does, you can
// repeat the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
// ...
}
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}
[Edited to fix: : SpecialValue, not = SpecialValue]
This is not possible. SpecialValueController is an "incomplete type" conceptually so the compiler cannot know. SpecialValueType, although it is constrained by SpecialValue, it is not known until it is determined by any adopting class. So it is a really placeholder with inadequate information. as?-ness cannot be checked.
You could have a base class that adopts SpecialController with a concrete type for SpecialValueController, and have multiple child classes that inherit from the adopting class, if you're still seeking a degree of polymorphism.
This doesn't work because SpecialController isn't a single type. You can think of associated types as a kind of generics. A SpecialController with its SpecialValueType being an Int is a completely different type from a SpecialController with its SpecialValueType being an String, just like how Optional<Int> is a completely different type from Optional<String>.
Because of this, it doesn't make any sense to cast to SpecialValueType, because that would gloss over the associated type, and allow you to use (for example) a SpecialController with its SpecialValueType being an Int where a SpecialController with its SpecialValueType being a String is expected.
As compiler suggests, the only way SpecialController can be used is as a generic constraint. You can have a function that's generic over T, with the constraint that T must be a SpecialController. The domain of T now spans all the various concrete types of SpecialController, such as one with an Int associated type, and one with a String. For each possible associated type, there's a distinct SpecialController, and by extension, a distinct T.
To draw out the Optional<T> analogy further. Imagine if what you're trying to do was possible. It would be much like this:
func funcThatExpectsIntOptional(_: Int?) {}
let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.
I have the following playground:
// : Playground - noun: a place where people can play
import Foundation
// Define a protocol
protocol MyProtocol
{
func getMeAString() -> String!
}
// Create a class that conforms to the protocol
class ClassThatConformsToProtocol: MyProtocol
{
func getMeAString() -> String! {
return "hey!"
}
}
// Define a function that takes a generic, but ensure the generic conforms to the protocol
func logTheThing<T: MyProtocol>(theThing: T!)
{
theThing.getMeAString()
}
// Let's create an instance
let instanceOfClassThatConforms = ClassThatConformsToProtocol()
// We're now going to see if we can pass the object to our generic method
logTheThing(instanceOfClassThatConforms)
// It works!
// Let's create another method, but this one only knows the protocol its parameter conforms to, the in implementing class
func takeInAnObjectThatWeOnlyKnowItsProtocol(object:MyProtocol)
{
logTheThing(object) // error: cannot convert value of type 'MyProtocol' to expected argument type '_!'
}
As mentioned, I receive an error stating:
error: cannot convert value of type 'MyProtocol' to expected argument
type '_!'
For what reason would I not be able to pass this conformed object into the generic method?
If the compiler knows an object conforms to the protocol, for what reason would I NOT be able to pass it into a generic method?
A protocol does not conform to itself. You must make this generic:
func takeInAnObjectThatWeOnlyKnowItsProtocol<T: MyProtocol>(object: T)
{
logTheThing(object)
}
To the next question: why doesn't a protocol conform to itself? Because it doesn't. Eventually it probably will, but it doesn't today.
That said, given this specific code, there's no reason to make this generic at all. Just pass the protocol and it'll do exactly what you want:
func logTheThing(theThing: MyProtocol) {
theThing.getMeAString()
}
func takeInAnObjectThatWeOnlyKnowItsProtocol(object: MyProtocol) {
logTheThing(object)
}
Passing object: MyProtocol means "any type that conforms to MyProtocol" and that matches in both places. Passing <T: MyProtocol>(object: T) means "a specific, concrete type that conforms to MyProtocol" and "any type that conforms to MyProtocol" is not a "specific, concrete type" and so fails (today; again, they'll probably fix that some day).
(Unrelated note: There is never a good reason to return String! in Swift. Just return String. And you'd get a better error message and fewer other little problems if you don't use T! and just use T in your logging call. Unless you're bridging to ObjC, there is almost never a reason to pass or return ! types. They only make sense in pure Swift for properties.)
I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.
I'm running into a few oddities while trying to conform to StringLiteralConvertible:
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
init(stringLiteral value: StringLiteralType){
name = n
}
init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
name = n
}
init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
name = n
}
}
var ironMan = Person(name: "Tony Stark")
var spiderMan: Person = "Peter Parker"
I implemented both the ExtendedGraphemeClusterLiteralConvertible and the UnicodeScalarLiteralConvertible protocols (whatever on Earth that means).
However I still got errors and had to provide definitions for both ExtendedGraphemeClusterLiteralType and UnicodeScalarLiteralType:
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
Why did I have to provide this, if it's already in the standard header???
The compiler considered it still had the right to complain and forced me to add the required keyword to the inits, even though the definition of the protocols does not include the required keyword! Why????
The following code compiles, but I don't understand why the first version didn't compile!
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
required convenience init(stringLiteral value: StringLiteralType){
self.init(name: value)
}
required convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
self.init(name: value)
}
required convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
self.init(name: value)
}
}
Swift, if you like the compiler to yell at you, you're gonna love it! :-P
Why typealiases:
The answer to your first question lies in the fact that Swift protocols that have associated types can be considered abstract, and need to be bound to a specific type. Associated types are an alternative to using a more specialized approach like class MyClass : IsCompatibleWith<Int> {}. I think the reason for this is to have common hierarchies, but this limitation also causes many other headaches. No matter what though, it is reasonable and expected that you need to specify the type to which the protocol is bound. In Swift, you do this by type-aliasing associated type to the one you want (String in your case).
Also note, that the associated type is not specified in the protocol, the protocol only specifies that you must define it. That said, unless you have conflicting methods on your class, the compiler can generally infer the associated type (read this in the docs somewhere).
Required initializers:
It appears that initializers specified in protocols are required by default / nature. The documentation says that you can implement them as designnated or convenience, but that either way they are required:
Class Implementations of Protocol Initializer Requirements
You can implement a protocol initializer requirement on a conforming
class as either a designated initializer or a convenience initializer.
In both cases, you must mark the initializer implementation with the
required modifier:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
The use of the required modifier ensures that you provide an explicit
or inherited implementation of the initializer requirement on all
subclasses of the conforming class, such that they also conform to the
protocol.
From my reading though, it answers what but not why.