evermeer / EVReflection

Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift
Other
965 stars 119 forks source link

Generic Type #13

Closed AhmedZahraz closed 9 years ago

AhmedZahraz commented 9 years ago

Hi, I'im trying to parse jsonString to an object that contains a generic type but it's not working. This is my example :

public class A: EVObject {

var test : String = ""
var test2 : String = ""
var data: T? = nil

}

public class B: EVObject {
var test3 : String = "" var test4 : String = ""
}

evermeer commented 9 years ago

This looks like a swift bug / missing feature where there is no workaround possible. I created a unit test for this. I will do some more tests and then push this test to github. Maybe you can confirm that the test that I created is how you wanted to use generics?

The problem is that a generic class is not key value coding complient. So it's not possible to set any of the properties using yourObject.setValue(value, forKey: key) Then for some reason it's also not executing the setValue forUndefinedKey on the object itself. When adding a base class with a setValue forUndefinedKey, then the calls will come in there.

I will create an Apple bug report for this.

class Workarounds2Tests: XCTestCase {
    func testGenerics() {
        let json:String = "{\"test\":\"test\", \"data\":\"data\"}"
        let a = A<NSString>(json:json)
        XCTAssertEqual(a.test, "test", "test should contin test")
        XCTAssertEqual(a.data as! String, "data ", "data should contin data")
    }
}

public class A<T where T:NSObject>: GenericsBase {
    var test : String = ""
    var data: T = T()

    // Handling the setting of non key-value coding compliant properties
    override public func setValue(value: AnyObject!, forUndefinedKey key: String) {
        switch key {
        case "test":
            test = value as! String
        case "data":
            data = value as! T
        default:
            println("---> setValue for key '\(key)' should be handled.")            
        }
    }
}

public class GenericsBase: EVObject {
    override public func setValue(value: AnyObject!, forUndefinedKey key: String) {
        println("---> GenericsBase: setValue for key '\(key)' should be handled.")
    }
}
evermeer commented 9 years ago

Unit test can be found at: https://github.com/evermeer/EVReflection/blob/master/EVReflection/EVReflectionTests/SwiftGenericsBug.swift

An Apple bug report has been posted with the ID 22218794

evermeer commented 9 years ago

I succeeded creating a workaround for this. I will push an update later today. For solving this issue you will get code like this:

class WorkaroundSwiftGenericsTests: XCTestCase {
    func testGenerics() {
        let json:String = "{\"test\":\"test\", \"data\":\"data\"}"
        let a = MyGenericObject<NSString>(json: json)
        XCTAssertEqual(a.test, "test", "test should contin test")
        XCTAssertEqual(a.data as! String, "data", "data should contin data")
    }
}

// Only put the generic properties in this class. put the rest in a base class
public class MyGenericObject<T where T:NSObject>: MyGenericBase, EVGenericsKVC {
    var data: T = T()

    public func genericSetValue(value: AnyObject, forKey key: String) {
        switch key {
        case "data":
            data = value as! T
        default:
            println("---> setValue '\(value)' for key '\(key)' should be handled.")
        }
    }
}

// Put the rest of the properties in a base class like this. Otherwise you have to handle each in the genericSetValue
public class MyGenericBase: EVObject {
    var test : String = ""
}
evermeer commented 9 years ago

I have pushed the update to GitHub. You have to use version 2.5.1 if you want to implement this workaround.

evermeer commented 9 years ago

i have renamed the method in the protocol. Now instead of implementing genericSetValue you can use override public func setValue(value: AnyObject!, forUndefinedKey key: String) { So now it's the same as the other situations where key value coding is not supported.

evermeer commented 9 years ago

Since this is a Swift limitation / bug and there is a workaround which is added as a unit test, I am closing this issue.

AhmedZahraz commented 9 years ago

Thanks @evermeer it's working now !