Redeclaring members in an extension hides the original member *sometimes*. Why? - swift

By chance, I discovered that you can do this without the compiler complaining:
extension Date {
var timeIntervalSinceNow: TimeInterval {
return 1000
}
}
What's weirder, is that this actually evaluates to 1000:
Date().timeIntervalSinceNow
The extension seems to hide the original member.
So I tried to do this with my own class:
class A {
var a: String {
return "A"
}
}
extension A {
var a: String {
return "a"
}
}
and it fails to compile: "invalid redeclaration of 'a'".
I observed that this does not affect the usage of the original member through a protocol, which is expected behaviour of hiding:
extension Date {
var description: String {
return "XXXX"
}
}
let date: CustomStringConvertible = Date()
date.description // normal date
Date().description // "XXXX"
Can you explain why does the bullet pointed phenomena occur?

This works because you are declaring this extension in a separate module from the original variable declaration.
Across modules a variable name can be overloaded but in my mind this has been a shortcoming of Swift as there is currently no way to explicitly state which module declaration it is that you want.

Related

Understanding EnvironmentValues syntax

I was spending some time trying to understand the syntax used in EnvironmentValues and I was hoping someone could point out if I have made any errors.
extension EnvironmentValues {
var isSensitive: Bool {
get { self[SensitiveKey.self] }
set { self[SensitiveKey.self] = newValue }
}
}
My understanding is that the the body of the getter and setter is using subscript syntax where the first self is referring to the EnvironmentValues dictionary as an object. The square brackets allows for a key to be passed into this object. The key itself is a metatype instance of SensitiveKey which is a previously defined EnvironmentKey. It allows access to any type properties and methods, which in this case would be the required defaultValue for the EnvironmentKey.
Have I understood this correctly? I'd really appreciate if you could clarify any misunderstandings I have here.
Cheers.
You are close. Per the Swift Documentation:
private struct SensitiveKey: EnvironmentKey {
static let defaultValue: String = "SensitiveKey" // the default could be anything unique
}
extension EnvironmentValues {
var isSensitive: String {
get { self[SensitiveKey.self] }
set { self[SensitiveKey.self] = newValue }
}
}
extension View {
func isSensitive(_ isSensitive: String) -> some View {
environment(\.isSensitive, isSensitive)
}
}
The key is just an EnvironmentKey, not a meta type for isSensitive. It is literally the key to the dictionary value in EnvironmentValues. defaultValue is a poor choice of name that implies mutability as the key itself is immutable.
Also, when you see .self, it is the current instance of the type. .Self would refer to the type.

Swift: Generic's type protocol not being recognized

Long time listener, first time caller.
I'm getting the following error:
Cannot convert value of type MyClass<Model<A>, OtherClass> to expected argument type MyClass<Protocol, OtherClass>
Despite the fact that MyClass<T> conforms to Protocol
I've attached a snippet that can be run in Playgrounds that resembles what I am actually trying to achieve.
protocol DisplayProtocol {
var value: String { get }
}
class DataBundle<T: CustomStringConvertible>: DisplayProtocol {
var data: T
var value: String {
return data.description
}
init(data: T) {
self.data = data
}
}
class Mapper<DisplayProtocol, Data> {
// ...
}
class MapperViewModel<Data> {
let mapper: Mapper<DisplayProtocol, Data>
init(mapper: Mapper<DisplayProtocol, Data>) {
self.mapper = mapper
}
}
let dataBundle = DataBundle<Int>(data: 100)
let mapper = Mapper<DataBundle<Int>, Bool>()
let viewModel = MapperViewModel<Bool>(mapper: mapper) // <- This fails w/error
Is this the expected behavior? If it is it feels like its breaking the contract of allowing me to have the DisplayProtocol as a type in Mapper.
This is caused by the fact that Swift generics are invariant in respect to their arguments. Thus MyClass<B> is not compatible with MyClass<A> even if B is compatible with A (subclass, protocol conformance, etc). So yes, unfortunately the behaviour is the expected one.
In your particular case, if you want to keep the current architecture, you might need to use protocols with associated types and type erasers.

Swift protocol settable property through a read-only property [duplicate]

This question already has an answer here:
Swift: Failed to assign value to a property of protocol?
(1 answer)
Closed 6 years ago.
Can someone please tell me why Swift has to call the setter of a property when it's only being used to access an object (a protocol) in order to set one of its properties? This first example shows the error I get if I don't declare the indirect object as settable:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
// Note: Not settable
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!" // Error: Cannot assign to property : 'test' is a get-only property
print(child.test.name)
If I give it a setter, it compiles and works but it calls the setter:
protocol AProtocol {
var name: String { get set }
}
class AnImplementation: AProtocol {
var name = ""
}
class AParent {
var test = AnImplementation()
}
class AChild {
var parent: AParent!
var test: AProtocol {
get { return parent.test }
set(newTest) { print("Shouldn't be here!") }
}
}
var parent = AParent()
var child = AChild()
child.parent = parent
child.test.name = "Hello world!"
print(child.test.name)
Output is:
Shouldn't be here!
Hello world!
I'm not sure what I'm not understanding here. I assume I can just give it an empty setter, but I'd like to understand the reason for it.
Any information is much appreciated!
Change your protocol declaration to this:
protocol AProtocol:class {
var name: String { get set }
}
Otherwise, it is taken by default as a value type. Changing a value type's property replaces the value type instance (as shown by the setter observer). And you can't do that if the reference is a let reference.
This is probably caused by the fact that the compiler doesn't know whether AChild.test is a class or a value type. With classes there is no problem but with value types assigning to name would also create an assignment to test (value-copy behavior). Marking APProtocol as class protocol will fix the problem.
To expand, when the compiler is not sure whether test is a value or a class type, it will use the following rewrite of child.test.name = "Hello world!":
var tmp = child.test
tmp.test = "Hello world!"
child.test = tmp
because that will work for both class and value types.

In swift, why can I set a computed property of a polymorphic variable via optional chaining, but not on an unwrapped optional?

I ran into what I think is a strange error in may app. At the bottom of this question is complete code the reproduces what I am seeing in my app, but here is a quick demonstration.
I create two instances of the same class, one is declared as an optional conforming to a protocol the other as an optional of a concrete class
For both I can set the computed property via option chaining ie:
anOptionalInstance?.someComputedProperty = ....
for the concrete version I can set the property by unwrapping the optional
if let anInstance = anOptionalInstance {
anInstance.someComputedProperty = ....
}
For the polymorphic version, I get an error message that says I can't set the property on the instance.
Below is a complete file that reproduces the issue I am seeing.
Can anyone explain what is happening here?
struct MyStruct {
var someMember: String
}
protocol MyProtocol {
var myVar: MyStruct { get set }
}
class MyType: MyProtocol {
var myVar: MyStruct {
get {
return MyStruct(someMember: "some string")
}
set {
println(newValue)
}
}
}
class UsingClass {
var anInstanceOfMyType: MyProtocol?
var anOtherInstanceOfMyType: MyType?
func someMethod() {
anInstanceOfMyType = MyType()
anInstanceOfMyType?.myVar = MyStruct(someMember: "blah")
if let anInstanceOfMyType = anInstanceOfMyType {
// The following line produces this error :Cannot assign to 'myVar' in 'anInstanceOfMyType'
anInstanceOfMyType.myVar = MyStruct(someMember: "blah blah")
}
anOtherInstanceOfMyType = MyType()
anOtherInstanceOfMyType?.myVar = MyStruct(someMember: "blah")
if let anOtherInstanceOfMyType = anOtherInstanceOfMyType {
anOtherInstanceOfMyType.myVar = MyStruct(someMember: "blah blah")
}
}
}
The problem does happen because you are trying to change the property of the constant anInstanceOfMyType which type is MyProtocol.
1. Why anInstanceOfMyType is a constant?
At the first line of UsingClass, anInstanceOfMyType is actually declared as var. However with the Conditional Unwrapping a constant with name anInstanceOfMyType is created, and you are trying to change a property of that constant
2. Ok but anInstanceOfMyType references an instance of a class, so I should be able to change its properties even if it's a constant
Since anInstanceOfMyType has MyProtocol as type, it could contain a struct or a reference an instance of a class.
So the compiler does apply the safer approach and avoid you to change its properties.
Solution
Limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols:
protocol MyProtocol: class {
var myVar: MyStruct { get set }
}
or
If MyProtocol is updated to extend AnyObject
protocol MyProtocol : AnyObject {
var myVar: MyStruct { get set }
}
then becomes clear that anInstanceOfMyType must refer an instance of a class, in this case your code does compile.

How to specify multiple inter-dependent associated types in swift protocols?

How do I specify multiple associated types in a protocol with added dependencies on their inner associated types? For example:
protocol CombinedType
{
typealias First: FirstType
typealias Second: SecondType
var first: First { get }
var second: Second { get }
}
protocol FirstType
{
typealias Value
var value: Value { get }
}
protocol SecondType
{
type alias Value
var value: Value { get }
}
I need to specify a limitation on the CombinedType protocol that would make it so FirstType.Value == SecondType.Value much like in a where clause in generics.
Without that limitation, the code:
func sum<Combined: CombinedType>(combined: Combined) -> Combined.FirstType.Value
{
return combined.first + combined.second
}
produces an error Combined.FirstType.Value is not compatible with Combined.SecondType.Value.
But if I explicitly add the where clause to the function, it compiles:
func sum<Combined: CombinedType where Combined.FirstType.Value == Combined.SecondType.Value>(combined: Combined) -> Combined.FirstType.Value
{
return combined.first + combined.second
}
The problem is that this where needs to be copied everywhere, which produces a ton of boilerplate and it gets even worse when using CombinedType inside another protocol, in which case the where clause gets larger and larger.
My question is: can I somehow include that limitation in the protocol itself?