Swift cannot understand Class<String> as Class<AnyHashable> - swift

Is there a good reason why this fails:
class Test<T: Hashable> {}
var d: Test<AnyHashable>? = nil
let t = Test<String>()
d = t // Cannot assign value of type 'Test<String>' to type 'Test<AnyHashable>?'
while this obviously works ?
var d: AnyHashable? = nil
let t = "123"
d = t // Works fine

Related

Synchronising combined Properties in ReactiveSwift

I'm considering converting a project using my own custom signal framework to use ReactiveSwift instead, but there is a fundamental issue I've never figured out how to resolve in ReactiveSwift:
As a simplified example, let's say you have two mutable properties:
let a = MutableProperty<Int>(1)
let b = MutableProperty<Int>(2)
Then, we derive a property that combines both to implement our logic:
let c = Property.combineLatest(a, b).map { a, b in
return a + b
}
Later, we receive some information that causes us to update the values of both a and b at the same time:
a.value = 3
b.value = 4
The problem now is that c will inform its listeners that it has the values 3 -> 5 -> 7. The 5 is entirely spurious and does not represent a valid state, as we never wanted a state where a was equal to 3 and b was equal to 2.
Is there a way around this? A way to suppress updates to a Property while updating all of its dependencies to new states, and only letting an update through once you are done?
combineLatest‘s fundamental purpose is to send a value when either of its upstream inputs send a new value, so I don’t think there’s a way to avoid this issue if you want to use that operator.
If it’s important that both values update truly simultaneously then consider using a MutableProperty<(Int, Int)> or putting the two values in a struct. If you give a little more context about what you’re actually trying to accomplish then maybe we could give a better answer.
Pausing Updates
So I really don't recommend doing something like this, but if you want a general purpose technique for "pausing" updates then you can do it with a global variable indicating whether updates are paused and the filter operator:
let a = MutableProperty<Int>(1)
let b = MutableProperty<Int>(2)
var pauseUpdates = false
let c = Property.combineLatest(a, b)
.filter(initial: (0, 0)) { _ in !pauseUpdates }
.map { a, b in
return a + b
}
func update(newA: Int, newB: Int) {
pauseUpdates = true
a.value = newA
pauseUpdates = false
b.value = newB
}
c.producer.startWithValues { c in print(c) }
update(newA: 3, newB: 4)
But there are probably better context-specific solutions for achieving whatever you are trying to achieve.
Using a sampler to manually trigger updates
An alternate solution is to use the sample operator to manually choose when to take a value:
class MyClass {
let a = MutableProperty<Int>(1)
let b = MutableProperty<Int>(2)
let c: Property<Int>
private let sampler: Signal<Void, Never>.Observer
init() {
let (signal, input) = Signal<Void, Never>.pipe()
sampler = input
let updates = Property.combineLatest(a, b)
.map { a, b in
return a + b
}
.producer
.sample(with: signal)
.map { $0.0 }
c = Property(initial: a.value + b.value, then: updates)
}
func update(a: Int, b: Int) {
self.a.value = a
self.b.value = b
sampler.send(value: ())
}
}
let x = MyClass()
x.c.producer.startWithValues { c in print(c) }
x.update(a: 3, b: 4)
Using zip
If a and b are always going to change together, you can use the zip operator which waits for both inputs to have new values:
let a = MutableProperty<Int>(1)
let b = MutableProperty<Int>(2)
let c = Property.zip(a, b).map(+)
c.producer.startWithValues { c in print(c) }
a.value = 3
b.value = 4
Use zip with methods for each type of update
class MyClass {
let a = MutableProperty<Int>(1)
let b = MutableProperty<Int>(2)
let c: Property<Int>
init() {
c = Property.zip(a, b).map(+)
}
func update(a: Int, b: Int) {
self.a.value = a
self.b.value = b
}
func update(a: Int) {
self.a.value = a
self.b.value = self.b.value
}
func update(b: Int) {
self.a.value = self.a.value
self.b.value = b
}
}
let x = MyClass()
x.c.producer.startWithValues { c in print(c) }
x.update(a: 5)
x.update(b: 7)
x.update(a: 8, b: 8)
Combining the values into one struct
I thought I would provide an example of this even though you said you didn't want to do it, because MutableProperty has a modify method that makes it less cumbersome than you might think to do atomic updates:
struct Values {
var a: Int
var b: Int
}
let ab = MutableProperty(Values(a: 1, b: 2))
let c = ab.map { $0.a + $0.b }
c.producer.startWithValues { c in print(c) }
ab.modify { values in
values.a = 3
values.b = 4
}
And you could even have convenience properties for directly accessing a and b even as the ab property is the source of truth:
let a = ab.map(\.a)
let b = ab.map(\.b)
Creating a new type of mutable property to wrap the composite property
You could create a new class conforming to MutablePropertyProtocol to make it more ergonomic to use a struct to hold your values:
class MutablePropertyWrapper<T, U>: MutablePropertyProtocol {
typealias Value = U
var value: U {
get { property.value[keyPath: keyPath] }
set {
property.modify { val in
var newVal = val
newVal[keyPath: self.keyPath] = newValue
val = newVal
}
}
}
var lifetime: Lifetime {
property.lifetime
}
var producer: SignalProducer<U, Never> {
property.map(keyPath).producer
}
var signal: Signal<U, Never> {
property.map(keyPath).signal
}
private let property: MutableProperty<T>
private let keyPath: WritableKeyPath<T, U>
init(_ property: MutableProperty<T>, keyPath: WritableKeyPath<T, U>) {
self.property = property
self.keyPath = keyPath
}
}
With this, you can create mutable versions of a and b that make it nice and easy to both get and set values:
struct Values {
var a: Int
var b: Int
}
let ab = MutableProperty(Values(a: 1, b: 2))
let a = MutablePropertyWrapper(ab, keyPath: \.a)
let b = MutablePropertyWrapper(ab, keyPath: \.b)
let c = ab.map { $0.a + $0.b }
c.producer.startWithValues { c in print(c) }
// Update the values individually, triggering two updates
a.value = 10
b.value = 20
// Update both values atomically, triggering a single update
ab.modify { values in
values.a = 30
values.b = 40
}
If you have the Xcode 11 Beta installed, you can even use the new key path based #dynamicMemberLookup feature to make this more ergonomic:
#dynamicMemberLookup
protocol MemberAccessingProperty: MutablePropertyProtocol {
subscript<U>(dynamicMember keyPath: WritableKeyPath<Value, U>) -> MutablePropertyWrapper<Value, U> { get }
}
extension MutableProperty: MemberAccessingProperty {
subscript<U>(dynamicMember keyPath: WritableKeyPath<Value, U>) -> MutablePropertyWrapper<Value, U> {
return MutablePropertyWrapper(self, keyPath: keyPath)
}
}
Now instead of:
let a = MutablePropertyWrapper(ab, keyPath: \.a)
let b = MutablePropertyWrapper(ab, keyPath: \.b)
You can write:
let a = ab.a
let b = ab.b
Or just set the values directly without creating separate variables:
ab.a.value = 10
ab.b.value = 20

Swift 3 - Can't subtract variables: Ambiguous reference to member [subtraction] [duplicate]

I am trying to concatenate multiple strings in swift 3:
var a:String? = "a"
var b:String? = "b"
var c:String? = "c"
var d:String? = a! + b! + c!
When compiling I get the following error:
error: cannot convert value of type 'String' to specified type 'String?'
var d:String? = a! + b! + c!
~~~~~~~~^~~~
This used to work in swift 2. I am not sure why it doesn't work anymore.
Bug report filed by OP:
SR-1122: Failure to typecheck chain of binary operators on force-unwrapped values
Which has been resolved (fix commited to master Jan 3 2017), and should hence no longer be an issue in upcoming Swift 3.1.
This seems to be a bug (not present in Swift 2.2, only 3.0) associated with the case of:
Using the forced unwrapping operator (!) for at least 3 terms in an expression (tested using at least 2 basic operators, e.g. + or -).
For some reason, given the above, Swift messes up type inference of the expression (specifically, for the x! terms themselves, in the expression).
For all the examples below, let:
let a: String? = "a"
let b: String? = "b"
let c: String? = "c"
Bug present:
// example 1
a! + b! + c!
/* error: ambiguous reference to member '+' */
// example 2
var d: String = a! + b! + c!
/* error: ambiguous reference to member '+' */
// example 3
var d: String? = a! + b! + c!
/* error: cannot convert value of type 'String'
to specified type 'String?' */
// example 4
var d: String?
d = a! + b! + c!
/* error: cannot assign value of type 'String'
to specified type 'String?' */
// example 5 (not just for type String and '+' operator)
let a: Int? = 1
let b: Int? = 2
let c: Int? = 3
var d: Int? = a! + b! + c!
/* error: cannot convert value of type 'Int'
to specified type 'Int?' */
var e: Int? = a! - b! - c! // same error
Bug not present:
/* example 1 */
var d: String? = a! + b!
/* example 2 */
let aa = a!
let bb = b!
let cc = c!
var d: String? = aa + bb + cc
var e: String = aa + bb + cc
/* example 3 */
var d: String? = String(a!) + String(b!) + String(c!)
However as this is Swift 3.0-dev, I'm uncertain if this is really a "bug", as well as what's the policy w.r.t. reporting "bugs" in a not-yet-production version of code, but possibly you should file radar for this, just in case.
As for answering your question as how to circumvent this issue:
use e.g. intermediate variables as in Bug not present: Example 2 above,
or explicitly tell Swift all terms in the 3-term expression are strings, as in Bug not present: Example 3 above,
or, better yet, use safe unwrapping of your optional, e.g. using optional binding:
var d: String? = nil
if let a = a, b = b, c = c {
d = a + b + c
} /* if any of a, b or c are 'nil', d will remain as 'nil';
otherwise, the concenation of their unwrapped values */
Swift 3
let q: String? = "Hello"
let w: String? = "World"
let r: String? = "!"
var array = [q, w, r]
print(array.flatMap { $0 }.reduce("", {$0 + $1}))
// HelloWorld!
let q: String? = "Hello"
let w: String? = nil
let r: String? = "!"
var array = [q, w, r]
print(array.flatMap { $0 }.reduce("", {$0 + $1}))
// Hello!
func getSingleValue(_ value: String?..., seperator: String = " ") -> String? {
return value.reduce("") {
($0) + seperator + ($1 ?? "")
}.trimmingCharacters(in: CharacterSet(charactersIn: seperator) )
}
let val: String? = "nil"
val.flatMap({(str: String) -> String? in
return str + "value"
})
var a:String? = "a"
var b:String? = "b"
var c:String? = "c"
var d:String? = ""
let arr = [a,b,c]
arr.compactMap { $0 }.joined(separator: " ")
compactMap be used to filter out nil values from flattened arrays

How to use generic function to tear down swift's optional pyramid of doom

Instead of using multiple optional bindings, we can define a function to tear down optional pyramid of doom.
func if_let<T, U, V> (a: T?, _ b: U?, _ c: V?, fn:(T, U, V) -> () ){
if let a = a {
if let b = b {
if let c = c {
fn(a, b, c)
}
}
}
}
Then I can write like this:
var s1: String? = "s11"
var s2: String? = "s22"
var s3: String? = "s33"
if_let(s1, s2, s3) { s1, s2, s3 in
print(("\(s1) - \(s2) - \(s3)"))
}
However, the problem is how to make this if_let function more generic so that it can accept any number of arguments. My implementation is like this:
func if_let<T> (values: T?..., fn:(params: [T]) -> ()) {
for value in values {
guard value != nil else { return }
}
let unwrappedArray = values.map{ $0! }
fn(params: unwrappedArray)
}
I tried to map the array and get a new one with all elements unwrapped and then call the fn. But when I ran the test again, I got a compile error:
Cannot convert value of type String? to expected argument type '_?'
Can anyone explain and fix this error?
The problem is that your second implementation of if_let no longer takes as a final parameter a function of type (T,U,V)->(). It now needs a function of type ([T])->(). If you call it with one, it compiles:
if_let(s1, s2, s3) { args in // or: (args: [String])->() in
print("\(args[0]) - \(args[1]) - \(args[2])")
}
A relevant note, rather than an answer to the specific question: with Swift 2, you needn't enter the pyramid of doom no more
let a: String? = nil
let b: Int? = nil
let c: Double? = nil
// possible mutate...
if let a = a, b = b, c = c {
// do something with shadow vars
}

Nicer syntax for ternary with a let?

Is there a nicer way to do the assignment to DEF in the following example? I want to convert type A to Type B, but still preserve the nil possibility whenever I can.
Can't seem to stumble into a better way of doing this, however. Suggestions?
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue != nil ? Int(someValue) : nil
}
}
Swift 1:
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue.map{Int($0)}
}
}
Swift 2:
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue.map(Int.init)
}
}
map() takes an optional, unwraps it, and applies a function to it. If the optional resolves to nil, map() returns nil.
You are describing optional map:
var i: Int? = 2
let j = i.map { $0 * 2 } // j = .Some(4)
i = nil
let k = i.map { $0 * 2 } // k = nil
Think of this map like array or other collection map, where optionals are collections that have either zero (nil) or one (non-nil) element.
Note, if the operation you want to perform itself returns an optional, you need flatMap to avoid getting a double-optional:
let s: String? = "2"
let i = s.map { Int($0) } // i will be an Int??
let j = s.flatMap { Int($0) } // flattens to Int?

Swift Cast Generics Type

I'm trying to cast a generic type to its super class.
class Foo : NSObject {
}
class Test<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
let c = Test(Foo())
let v = c as Test<AnyObject>
But on the line
let v = c as Test<AnyObject>
I get 'Foo' is not identical to 'AnyObject'
While I can do that with built-in Arrays
let array1 = [Foo(), Foo()]
let array2 = array1 as [AnyObject]
Arrays are special-cased in Swift to be able to have this behaviour. You won't be able to replicate it with your own classes, unfortunately.
To get similar behaviour, you could give your type another init method:
class Test<T> {
let value: T
init(_ value: T) {
self.value = value
}
init<U>(other: Test<U>) {
// in Swift 1.2 you will need as! here
self.value = other.value as T
}
}
let c = Test(Foo())
let v = Test<AnyObject>(other: c) // this will work ok
Note though that this is unsafe (i.e. will assert at runtime) in cases where you want to convert to an incompatible type (and the alternative, a failable initializer, will also have complications)