You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import Foundation
class MyStringCollection: NSObject {
@objc var strings: Set<String> { return _strings }
@objc class var automaticallyNotifiesObserversOfStrings: Bool { return false }
func add(_ string: String) {
if _strings.contains(string) { return }
let keyPath = \MyStringCollection.strings
let mutation = NSKeyValueSetMutationKind.union
let set: Set<String> = [string]
willChangeValue(for: keyPath, withSetMutation: mutation, using: set)
_strings.insert(string)
didChangeValue(for: keyPath, withSetMutation: mutation, using: set)
}
private var _strings = Set<String>()
}
But the compiler rejects this example:
withSetMutationBug.swift:15:73: error: cannot convert value of type 'Set<String>' to expected argument type 'Set<Set<String>>'
willChangeValue(for: keyPath, withSetMutation: mutation, using: set)
^~~
withSetMutationBug.swift:17:72: error: cannot convert value of type 'Set<String>' to expected argument type 'Set<Set<String>>'
didChangeValue(for: keyPath, withSetMutation: mutation, using: set)
The problem is that the keyPath argument's generic type is KeyPath<Self, Value> and the set argument's generic type is Set<Value>. Based on the keyPath, Value needs (in my example) to be Set<String> but based on the set, Value needs to be just String.
The methods need to be changed to have usable signatures. This should work:
public func willChangeValue<Setlike, Value>(for keyPath: __owned KeyPath<Self, Setlike>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void where Setlike: Collection, Setlike.Element == Value {
(self as! NSObject).willChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}
public func didChangeValue<Setlike, Value>(for keyPath: __owned KeyPath<Self, Setlike>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void where Setlike: Collection, Setlike.Element == Value {
(self as! NSObject).didChangeValue(forKey: _bridgeKeyPathToString(keyPath), withSetMutation: mutation, using: set)
}
The text was updated successfully, but these errors were encountered:
Seems like an accurate description to me (whoops!). This is source-breaking, but in practice the current signature seems impossible to use correctly, so it shouldn't actually break any working apps.
Rob's proposed signature is better written without the Value parameter at all:
public func willChangeValue<Setlike>(for keyPath: __owned KeyPath<Self, Setlike>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Setlike.Element>) -> Void where Setlike: Collection {
Another possible signature:
public func willChangeValue<Value>(for keyPath: __owned KeyPath<Self, Set<Value>>, withSetMutation mutation: NSKeyValueSetMutationKind, using set: Set<Value>) -> Void {
Environment
Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
Additional Detail from JIRA
md5: f4712f1ea33c722bda45fa956c7ddb3e
Issue Description:
The Swift Foundation overlay defines these two methods:
The signatures of these methods are wrong.
Here's an example of how they ought to be used:
But the compiler rejects this example:
The problem is that the
keyPath
argument's generic type isKeyPath<Self, Value>
and theset
argument's generic type isSet<Value>
. Based on thekeyPath
,Value
needs (in my example) to beSet<String>
but based on theset
,Value
needs to be justString
.The methods need to be changed to have usable signatures. This should work:
The text was updated successfully, but these errors were encountered: