Closed Ivorforce closed 5 years ago
A rough implementation might look like this:
class SimpleTransformer<Source, Dest : AnyObject>: ValueTransformer {
let there: (Any?) -> Any?
let back: ((Any?) -> Any?)?
override class func transformedValueClass() -> Swift.AnyClass { return Dest.self }
init(there: @escaping (Source?) -> Dest?, back: ((Dest?) -> Source?)? = nil) {
self.there = { there($0 as? Source) }
self.back = back != nil ? { back!($0 as? Dest) } : nil
}
override func transformedValue(_ value: Any?) -> Any? {
return there(value)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
return back!(value)
}
}
extension NSObject {
open func bind<Object, Source>(_ binding: NSBindingName, to observable: Object, withKey key: Defaults.Key<Source>, options: [NSBindingOption: Any] = [:], transform: ((Source) -> AnyObject?)? = nil, back: ((AnyObject?) -> Source)? = nil) {
let ftransform: (String?) -> AnyObject? = { value in
guard let value = value.map(decode) else {
return nil
}
return transform.map { $0(value) } ?? value as AnyObject
}
let fback: (AnyObject?) -> String? = { value in
guard let value = (back.map { $0(value) } ?? (value as? Source)) else {
return nil
}
return encode(value)
}
var options = options
options[.valueTransformer] = SimpleTransformer<String, AnyObject>(there: ftransform, back: fback)
bind(binding, to: observable, withKeyPath: key.name, options: options)
}
}
Personally I like passing transformers directly but obviously this can be accomplished with a usual ValueTransformer as well.
This might be used like:
button.bind(.value, to: AppDelegate.defaults, withKey: Defaults.Keys.something,
transform: { NSNumber(value: value.ordinal <= $0.ordinal) },
back: { ($0 as? NSNumber)?.boolValue == true ? value : prev }
)
With SwiftUI and Combine, there's no future for legacy Cocoa bindings, so I'm not really interested in maintaining code for this.
Closing this in favor of #23.
... which is extremely useful for GUI. An "emulation" instead using kvc would also suffice for most purposes, I think.