Skip to content

Instantly share code, notes, and snippets.

@jeffreybergier
Created October 27, 2016 16:56
Show Gist options
  • Save jeffreybergier/4482b0ab0357b08558a09501d60b6d1d to your computer and use it in GitHub Desktop.
Save jeffreybergier/4482b0ab0357b08558a09501d60b6d1d to your computer and use it in GitHub Desktop.
// I've tried several combos
// I'll list each one and the error it gives
// #1 Playground execution failed: error: Untitled Page 2.xcplaygroundpage:4:24: error: cannot specialize non-generic type 'MyWeirdDictionaryDelegate'
protocol MyWeirdDictionaryDelegate: class {
associatedtype K
func willSet(value: K, forKey key: String) -> K?
func willReturn(value: K?, forKey: String) -> K?
}
class MyWeirdDictionary<T> {
weak var delegate: MyWeirdDictionaryDelegate<T>?
private var data = [String : T]()
func set(value: T, forKey key: String) {
self.data[key] = self.delegate?.willSet(value: value, forKey: key) ?? value
}
func value(forKey key: String) -> T? {
let existingValue = self.data[key]
return self.delegate?.willReturn(value: existingValue, forKey: key) ?? existingValue
}
}
// #2 Playground execution failed: error: Untitled Page 2.xcplaygroundpage:2:24: error: protocol 'MyWeirdDictionaryDelegate' can only be used as a generic constraint because it has Self or associated type requirements
protocol MyWeirdDictionaryDelegate: class {
associatedtype K
func willSet(value: K, forKey key: String) -> K?
func willReturn(value: K?, forKey: String) -> K?
}
class MyWeirdDictionary<T> {
weak var delegate: MyWeirdDictionaryDelegate?
private var data = [String : T]()
func set(value: T, forKey key: String) {
self.data[key] = self.delegate?.willSet(value: value, forKey: key) ?? value
}
func value(forKey key: String) -> T? {
let existingValue = self.data[key]
return self.delegate?.willReturn(value: existingValue, forKey: key) ?? existingValue
}
}
// #3 Protocols do not allow generic parameters; use associated types instead
protocol MyWeirdDictionaryDelegate<K>: class {
func willSet(value: K, forKey key: String) -> K?
func willReturn(value: K?, forKey: String) -> K?
}
class MyWeirdDictionary<T> {
weak var delegate: MyWeirdDictionaryDelegate<T>?
private var data = [String : T]()
func set(value: T, forKey key: String) {
self.data[key] = self.delegate?.willSet(value: value, forKey: key) ?? value
}
func value(forKey key: String) -> T? {
let existingValue = self.data[key]
return self.delegate?.willReturn(value: existingValue, forKey: key) ?? existingValue
}
}
// #4 this is how I got it to work. replacing the protocol with an empty superclass and then subcalssing the delegate class to make my own custom delegate
class MyWeirdDictionaryDelegate<K> {
func willSet(value: K, forKey key: String) -> K? { return .none }
func willReturn(value: K?, forKey: String) -> K? { return .none }
}
class MyWeirdDictionary<T> {
weak var delegate: MyWeirdDictionaryDelegate<T>?
private var data = [String : T]()
func set(value: T, forKey key: String) {
self.data[key] = self.delegate?.willSet(value: value, forKey: key) ?? value
}
func value(forKey key: String) -> T? {
let existingValue = self.data[key]
return self.delegate?.willReturn(value: existingValue, forKey: key) ?? existingValue
}
}
@algal
Copy link

algal commented Oct 27, 2016

Here's one way:

class MyWeirdDictionary<T,D:MyWeirdDictionaryDelegate> where D.U == T {
  weak var delegate: D?
  private var data = [String : T]()
  func set(value: T, forKey key: String) {
    self.data[key] = self.delegate?.willSet(value: value, forKey: key) ?? value
  }
}

protocol MyWeirdDictionaryDelegate : class {
  associatedtype U
  func willSet(value:U,forKey key:String)  -> U?
}

If you don't want to make your dictionary generic over its value type and over the delegate type, then I suspect you need to use some form of type erasure.

@jeffreybergier
Copy link
Author

Thanks. I'll give that a shot. No I want the types to match on the delegate and delegating instance.

@jeffreybergier
Copy link
Author

jeffreybergier commented Oct 27, 2016

import Foundation

protocol MyWeirdDictionaryDelegate: class {
    associatedtype K
    func willSet(value: K, forKey key: String, weirdDictionary: MyWeirdDictionary<K, Self>) -> K?
    func willReturn(value: K?, forKey: String, weirdDictionary: MyWeirdDictionary<K, Self>) -> K?
}

class MyWeirdDictionary<T, D:MyWeirdDictionaryDelegate> where D.K == T {
    weak var delegate: D?
    private var data = [String : T]()
    func set(value: T, forKey key: String) {
        self.data[key] = self.delegate?.willSet(value: value, forKey: key, weirdDictionary: self) ?? value
    }
    func value(forKey key: String) -> T? {
        let existingValue = self.data[key]
        return self.delegate?.willReturn(value: existingValue, forKey: key, weirdDictionary: self) ?? existingValue
    }
}

final class MyAwesomeWeirdDictionaryDelegate: MyWeirdDictionaryDelegate {
    typealias K = Date
    func willSet(value: K, forKey key: String, weirdDictionary: MyWeirdDictionary<K, MyAwesomeWeirdDictionaryDelegate>) -> K? {
        print("Awesome Date: \(value)")
        return .none
    }
    func willReturn(value: K?, forKey: String, weirdDictionary: MyWeirdDictionary<K, MyAwesomeWeirdDictionaryDelegate>) -> K? {
        if let value = value {
            print("Getting Awesome Date: \(value)")
        } else {
            print("Couldn't find an awesome date :(")
        }
        return .none
    }
}

let storage = MyWeirdDictionary<Date, MyAwesomeWeirdDictionaryDelegate>()
let delegate = MyAwesomeWeirdDictionaryDelegate()
storage.delegate = delegate

storage.set(value: Date(), forKey: "Hi")
storage.value(forKey: "Hi")
storage.value(forKey: "Hello")

I had to add a Self requirement in the protocol so I could pass a pointer to the delegator through the delegate methods. So now the delegate class cannot be subclassed. It gives the error below. Its ok. I don't need to subclass the delegate.

Also, credit needs to go to Khanlou on his suggestion for changing the names of delegate methods to put the delegator argument at the end. I think it makes them look massively better. http://khanlou.com/2016/09/swifty-delegates/

Playground execution failed: error: Untitled Page 2.xcplaygroundpage:18:10: error: protocol 'MyWeirdDictionaryDelegate' requirement 'willSet(value:forKey:weirdDictionary:)' cannot be satisfied by a non-final class ('MyAwesomeWeirdDictionaryDelegate') because it uses 'Self' in a non-parameter, non-result type position
    func willSet(value: K, forKey key: String, weirdDictionary: MyWeirdDictionary<K, MyAwesomeWeirdDictionaryDelegate>) -> K? {
         ^

error: Untitled Page 2.xcplaygroundpage:22:10: error: protocol 'MyWeirdDictionaryDelegate' requirement 'willReturn(value:forKey:weirdDictionary:)' cannot be satisfied by a non-final class ('MyAwesomeWeirdDictionaryDelegate') because it uses 'Self' in a non-parameter, non-result type position
    func willReturn(value: K?, forKey: String, weirdDictionary: MyWeirdDictionary<K, MyAwesomeWeirdDictionaryDelegate>) -> K? {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment