Swift ValueTransformer old "good" code now complains with contemporary swift - swift

I have an old simple ValueTransformer. Written just following the conventional pattern to support a GUI interface to edit a specific encoded file format. I recently has cause to regenerate with current compiler and it is complaining bitterly about converting Bool.self to AnyClass. Yeah, OK, I understand that Bool is no longer a Class (if it ever was) it is a frozen Struct. So the question is that of, is there a Swift way of continuing to use this ValueTransformer for a struct rather than a class?
I can see an ugly solution of wrapping a Bool in a Class and using that but is poor design at best, but if needs must....
Am I missing something obvious in the ever changing world ?
The complaint from the the compiler is on the single line in transformedValueClass
return Bool.self as! AnyClass
Cast from 'Bool.Type' to unrelated type 'AnyClass' (aka 'AnyObject.Type') always fails
class StringToBoolTransformer : ValueTransformer
{
var boolValue : Bool?
override func transformedValue(_ value: Any?) -> Any? {
if let stringRep = value as? String
{
if stringRep.count == 1 {
boolValue = (stringRep == "1" ? true :(stringRep == "0" ? false: nil))
}
}
return boolValue
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
var boolAsString : String?
if let boolValue = value as? Bool {
boolAsString = boolValue ? "1" : "0"
}
return boolAsString
}
override class func transformedValueClass() -> AnyClass
{
return Bool.self as! AnyClass
}
override class func allowsReverseTransformation() -> Bool {
return true
}
}

(NS)ValueTransformer relies on Objective-C and the corresponding class of Bool is NSNumber.
I made the code a bit more contemporary 😉
class StringToBoolTransformer : ValueTransformer
{
override func transformedValue(_ value: Any?) -> Any? {
guard let stringRep = value as? String,
stringRep.count == 1,
["0","1"].contains(stringRep) else { return nil }
let boolValue = stringRep == "1"
return NSNumber(booleanLiteral: boolValue)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let number = value as? NSNumber else { return nil }
return number.boolValue ? "1" : "0"
}
override class func transformedValueClass() -> AnyClass
{
return NSNumber.self
}
override class func allowsReverseTransformation() -> Bool {
return true
}
}

In Swift Bool is a struct, NOT a class. So you can never cast it to AnyClass.
What you can try as a workaround is use NSNumber class as a storage for bool via NSNumber.init(value: Bool) and then return NSNumber.self from your implementation.

Related

How to make generic protocol with associatedtype not generic via protocol inheritance?

I have generic protocol with associated type and at some point would like to make it not generic. The same behaviour works fine with classes but unfortunately I can't use the same logic with protocols. For instance this is absolutely fine:
class Validator<Value> {
func validate(_ value: Value?) -> String? {
return nil
}
}
class StingValidator: Validator<String> {
}
class EmptyStringValidator: StingValidator {
override func validate(_ value: String?) -> String? {
return ((value?.isEmpty ?? true) == true) ? "Empty" : nil
}
}
func anyfunc() {
let validator: StingValidator = EmptyStringValidator()
}
But using protocols I am getting compile error
Protocol 'StringValidatorProtocol' can only be used as a generic constraint because it has Self or associated type requirements
protocol ValidatorProtocol {
associatedtype Value
func validate(_ value: Value?) -> String?
}
protocol StringValidatorProtocol: ValidatorProtocol where Value == String {
}
struct EmptyStringValidator: StringValidatorProtocol {
func validate(_ value: String?) -> String? {
return ((value?.isEmpty ?? true) == true) ? "Empty" : nil
}
}
func anyfunc() {
let validator: StringValidatorProtocol = EmptyStringValidator()
}
Any ideas? Is there any way to make generic protocol not generic? I would appreciate any help!
Making a protocol with associated type not generic is simply not possible and I think what you needed is more generics:
func anyfunc() {
let validator: StringValidatorProtocol = EmptyStringValidator()
}
with a free function you can't do what you're trying to do, but inside a class or a struct:
protocol ValidatorProtocol {
associatedtype Value
init()
func validate(_ value: Value?) -> String?
}
protocol StringValidatorProtocol: ValidatorProtocol where Value == String {
}
class EmptyStringValidator: StringValidatorProtocol {
required init() {
}
func validate(_ value: String?) -> String? {
return value?.isEmpty ?? true ? "Empty" : nil
}
}
class MyClass<Validator: ValidatorProtocol> {
static func validate(_ string: Validator.Value?) -> String? {
let validator: Validator = Validator()
return validator.validate(string)
}
}
MyClass<EmptyStringValidator>.validate("Hello World") // nil
MyClass<EmptyStringValidator>.validate("") // "Empty"
MyClass<EmptyStringValidator>.validate(nil) // "Empty"
I don't know what "make it not generic" means, but let us know via an edit if this is not enough for you:
The error you've shown can be solved with an opaque constant.
let validator: some StringValidatorProtocol = EmptyStringValidator()

Swift protocol with generic method: invalid redeclaration of implementation

I'm playing with some protocol/generic stuff in Swift and I'm curious why following code is refusing to be compiled:
protocol MyProtocol {
func value<T>() -> T
}
class StringImpl: MyProtocol {
var string: String
init() {
self.string = "..."
}
init(string: String) {
self.string = string
}
func value<String>() -> String {
return self.string as! String
}
}
class BoolImpl: MyProtocol {
var value: Bool
init() {
self.value = false
}
init(value: Bool) {
self.value = value
}
func value<Bool>() -> Bool {
return self.value as! Bool
}
}
with particular error
error: invalid redeclaration of 'value()'
func value<Bool>() -> Bool {
It may imply that I cannot have different implementations of protocol generic method, but I don't see clear reason why.
(I'm not speaking of force type cast as generic type shadows existing one)
P.S. For those who are curious I may tell that I'm trying to make protocol that is not messed with associatedtype and still is generic to some degree.
You have no problem the mistake is var value, and redeclaring a function with name of func value<Bool>, i just changed the variable name and it worked, the error clearly says
error: invalid redeclaration of 'value()'
class BoolImpl: MyProtocol {
var bool: Bool
init() {
self.bool = false
}
init(value: Bool) {
self.bool = value
}
func value<Bool>() -> Bool {
return self.bool as! Bool
}
}
error: invalid redeclaration of 'value()'
The thing is that. Your method has same name as your variable and it also returns the same type. So, compiler tells you that this isn't legal.
Anyway, you should think if you really need this method. You can add this value property to protocol and it also looks like you need associatedtype for your protocol:
protocol MyProtocol {
associatedtype T
var value: T { get set }
}
class StringImpl: MyProtocol {
typealias T = String
var value: T = "..."
init(string: T) {
value = string
}
}
class BoolImpl: MyProtocol {
typealias T = Bool
var value: T = false
init(value: T) {
self.value = value
}
}
then, if you need just value of your object, you can just get value property
someStringImpl.value
someBoolImpl.value
The problem is that 'value' variable name is the same as 'value' function name
class BoolImpl: MyProtocol {
var storedValue: Bool
init() {
self.storedValue = false
}
init(value: Bool) {
self.storedValue = value
}
func value<Bool>() -> Bool {
return self.storedValue as! Bool
}
}
Everyone seems to be missing the point here.
You seem to think that your adopters of
protocol MyProtocol {
func value<T>() -> T
}
...resolve the generic. They do not. They merely repeat the generic.
For example, think about your
func value<String>() -> String {
return self.string as! String
}
self.string is a string. So ask yourself why you have to say as! String. It’s because you are misusing String as a placeholder name just like T. You would get the same result using a nonsense word:
func value<Stringgg>() -> Stringgg {
return self.string as! Stringgg
}
That compiles too. You still haven’t resolved the generic, you merely changed its name. Your attempt to avoid an associated type has failed. Your code compiles but it can never run.

Swift indexOf, == operator override not getting called

I am getting frustrated with how Swift handles equality. Or I'm just missing something. How come these 2 indexOf DOESN'T work the same way?
let first = self.objects.indexOf(object) //This returns nil
let second = self.objects.indexOf{$0 == object} //This returns the index
My == override:
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.someProperty == rhs.someProperty
}
The == override doesn't get called in the first indexOf. Why is that so? This feels really dangerous.
(MyObject is a subclass of PFObject (Parse.com objects). I don't know if this is what's messing this up.)
Since your Object is subclass of PFObject, and PFObject is subclass of NSObject, and NSObject already confirm to Equatable by using the isEqual method to implement. So your == operator override is not working. You should override the isEqual method. (To be honest, it's horrible :(
sample code:
class Object: NSObject {
var value: Int
init(value: Int) {
self.value = value
}
override func isEqual(object: AnyObject?) -> Bool {
guard let obj = object as? Object else { return false }
return self.value == obj.value
}
}
// If your class is not inherit from any ObjC class:
//extension Object: Equatable {}
//func ==(lhs: Object, rhs: Object) -> Bool {
// return lhs.value == rhs.value
//}
let c = Object(value: 3)
let objs = [Object(value: 1), Object(value: 2), Object(value: 3)]
let index = objs.indexOf(c)
let index2 = objs.indexOf { $0 == c }
print(index) // Optional(2)
print(index2) // Optional(2)
The second indexOf method that you are using allows you to define a closure to indicate object equality (like you have already done). The first one according to the documentation:
Returns the first index where value appears in self or nil if value is not found.
Where value is the object you are passing as the parameter. Without seeing the implementation of this version I can only assume they compare if the two objects are equal in every way (all properties match). So in your case, if you only consider the objects equal if the somePropertys match, then you should be using the closure method with your overloaded operator.
static func == (left: MyClass, right: MyClass) -> Bool {
return left.attribute1 == right.attribute1
}
override func isEqual(_ object: Any?) -> Bool {
guard let right = object as? MyClass else {
return false
}
// call the == from above
// why couldn't apple do this ???
//
return self == right
}

Swift Protocol as Generic Parameter

Given this class:
class ServiceRegistry {
var store = [String : AnyObject]()
var allRegisteredType: [String] {
return store.map { $0.0 }
}
func registerInstance<T>(instance:AnyObject, forType type: T.Type) {
store[String(type)] = instance
}
func instanceForType<T>(type: T.Type) -> T? {
return store[String(type)] as? T
}
}
Is there a way I can enforce that T must be a Protocol, without using the #obj?
This is a modified version of my type assertion technique. I added the "as? AnyClass" assert so that the type can only be of protocol type. There might be a more elegant way of doing this but going through my notes and research about class assertion this is what I came up with.
import Foundation
protocol IDescribable:class{}
class A:IDescribable{}
class B:A{}
let a = A()
let b = B()
func ofType<T>(instance:Any?,_ type:T.Type) -> T?{/*<--we use the ? char so that it can also return a nil*/
if(instance as? T != nil && type as? AnyClass == nil){return instance as? T}
return nil
}
Swift.print(ofType(a,A.self))//nil
Swift.print(ofType(a,IDescribable.self))//A
Swift.print(ofType(b,B.self))//nil

Casting AnyObject to T

Code:
class User
{
class var BoolProperty: Bool
{
get {
var anyObject: AnyObject? = getValue("BoolProperty")
if let value = anyObject as? Bool {
return value
}
else {
return false
}
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
}
passes the test:
class UserTests: XCTestCase
{
func testFields()
{
User.BoolProperty = true
var result = User.BoolProperty
XCTAssertEqual(true, result)
}
}
but the following code doesn't pass the same test, which uses T instead of Bool for casting:
class User
{
class var BoolProperty: Bool
{
get {
return get("BoolProperty", defaultValue: false)
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
private class func get<T>(key: String, defaultValue: T) -> T
{
var anyObject: AnyObject? = getValue(key)
if let value = anyObject as? T {
return value
}
else {
return defaultValue
}
}
}
it seems, that for some reason if let value = anyObject as? T always returns false when casting to T.
In C# this is a classic example of working with untyped collections and I was wondering what's the right approach to achieve the same in Swift.
The problem is that an NSNumber is not a Bool. It looks like a Bool, and it can be converted to a Bool, but it's really a different type. Your anyObject is really an NSNumber, and asking for it to be as? T where T is Bool is too far. That's still likely a compiler bug, because it should be the same as a generic as without, but it's where the problem is happening.
You need to specialize the generic function to NSNumber:
get {
return Bool(get("BoolProperty", defaultValue: NSNumber(bool: false)))
}
It's still probably worth opening a radar. It shouldn't behave differently with and without the generic.
That said, part of the trouble is the use of AnyObject. A Bool is not an AnyObject. It's an Any. When you try to assign it to an AnyObject, it gets converted into an NSNumber. I don't know of any documentation of this; I've worked it out empirically in playgrounds. Compare the results of let a:AnyObject = false and let a:Any = false.
In theory, this should fix it all:
var anyObject: Any? = getValue(key)
But it currently crashes the compiler.