Why do I need to force cast a property to a generic method with the same signature as the property? - swift

This is my code:
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
enum SomeEnum {
case myValue
}
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: GenericClass<UITableViewCell>.SomeEnum) {}
}
Why do I get the compile error:
Cannot convert value of type 'GenericClass.SomeEnum' to expected
argument type 'GenericClass.SomeEnum'
Ofcourse, the fix would be adding the cast:
as! GenericClass<UITableViewCell>.SomeEnum
which results in this ugly code:
func callOtherClass() {
OtherClass.handle(property: enumProperty) as! GenericClass<UITableViewCell>.SomeEnum
}
But why do I need to cast? self is defined as GenericClass where T always is a UITableViewCell. The method handle expects that signature.
Is there any case this cast is needed because in some situations this will/can fail? I would not expect Swift just randomly asking me to insert a force cast. I expect Swift can just infer the types and sees it is safe, but somehow, Swift doesn't agree with me.

The problem here is that SomeEnum is actually GenericClass<T>.SomeEnum. There's no promise that T is exactly UITableViewCell, so it's not compatible with GenericClass<UITableViewCell> (generics are not covariant).
Typically in this case, what you want to do is move SomeEnum outside of GenericClass, since nothing about it is actually generic:
enum SomeEnum {
case myValue
}
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: SomeEnum) {}
}
But if there's a reason for it to be generic, see Robert Dresler's answer, which is how you would specialize the function correctly:
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}

Make your static method also generic and create generic constrain for parameter inheriting from UITableViewCell. Then use this generic parameter in method parameter
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}

Related

Why can’t I use a protocol required initializer when constraining protocol to a class?

Swift won’t let me compile the following code with the error message
Constructing an object of class type 'T' with a metatype value must use a 'required' initializer
on the indicated line. The used initializer is clearly required by the protocol. Moving the construction into the initializer causes the same issue. If I remove where Self: ChannelUser (and the code making use of its method) everything compiles. The issue can also be fixed by requiring the initializer in ChannelUser but that is obviously off the point.
Why is that the case? Am I doing something wrong or is this a bug?
class ChannelUser {
func ready() {
print("ready")
}
}
class A: ChannelUser { // Cannot be used with MockChannelUser because the initializer takes an Int
init(a: Int) {
// do something clever with a
}
}
class B: ChannelUser, MockableChannelUser { // Can be used with MockChannelUser
override required init() {}
}
protocol MockableChannelUser where Self: ChannelUser {
init()
}
struct MockChannelUser<T: MockableChannelUser> {
let user = T() // Error: Constructing an object of class type 'T' with a metatype value must use a 'required' initializer
init() {
user.ready()
}
}

How can I create a function in Swift that returns a Type which conforms to a protocol?

How can I create a function in Swift that returns a Type which conforms to a protocol?
Here is what I'm trying right now, but it obviously won't compile like this.
struct RoutingAction {
enum RoutingActionType{
case unknown(info: String)
case requestJoinGame(gameName: String)
case requestCreateGame(gameName: String)
case responseJoinGame
case responseCreateGame
}
// Any.Type is the type I want to return, but I want to specify that it will conform to MyProtocol
func targetType() throws -> Any.Type:MyProtocol {
switch self.actionType {
case .responseCreateGame:
return ResponseCreateGame.self
case .responseJoinGame:
return ResponseJoinGame.self
default:
throw RoutingError.unhandledRoutingAction(routingActionName:String(describing: self))
}
}
}
I would personally prefer returning an instance instead of a type but you can do it that way too. Here's one way to achieve it:
protocol MyProtocol:class
{
init()
}
class ResponseCreateGame:MyProtocol
{
required init() {}
}
class ResponseJoinGame:MyProtocol
{
required init() {}
}
enum RoutingActionType
{
case unknown(info: String),
requestJoinGame(gameName: String),
requestCreateGame(gameName: String),
responseJoinGame,
responseCreateGame
// Any.Type is the type I want to return, but I want to specify that it will conform to MyProtocol
var targetType : MyProtocol.Type
{
switch self
{
case .responseCreateGame:
return ResponseCreateGame.self as MyProtocol.Type
case .responseJoinGame:
return ResponseJoinGame.self as MyProtocol.Type
default:
return ResponseJoinGame.self as MyProtocol.Type
}
}
}
let join = RoutingActionType.responseJoinGame
let objectType = join.targetType
let object = objectType.init()
Note that your protocol will need to impose a required init() to allow creation of instances using the returned type.
Note2: I changed the structure a little to make my test easier but i'm sure you'll be able to adapt this sample to your needs.
Why do you not want to use simple:
func targetType() throws -> MyProtocol
?
EDIT:
I think you can't. Because if you return Type actually you return an instance of class Class and it can't conform your protocol. This runtime's feature was inherited from objective-c. You can see SwiftObject class.

Swift: Return class constrained with generic type

I have a basic generic class:
class SharedClass<T> {}
And a builder for it:
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return ...
}
}
What I want to be able to do, is return an instance that inherits SharedClass, and conforms to T. For example:
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.doIt()
Unfortunately, the Swift compiler complains that the returned type does not have a member named doIt.
Is there a way to achieve what I'm looking for?
I suspect it's not so much that you want the returned class to be constrained by the generic type, as you're asking the returned class to be an instance of the constrained type. In your snippet, you're expecting the unwrapped result to conform to MyObject. Taking this a step further, it means that the conformance of SharedClass is determined entirely from how it was constructed. As far as I know this isn't supported in Swift.
However, there's nothing stopping you having a member of SharedClass that is a T. Something along the lines of:
class SharedClass<T> {
var v : T?
}
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return SharedClass()
}
}
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.v?.doIt()

Returning a generic from a protocol-defined function in Swift

I declared a protocol with a generic function, but it seems that the type inference isn't working properly after implementing it.
protocol SearchableRealmModel {
static func search<Self: Object>(needle: String) -> Results<Self>?
}
class Thing: Object, SearchableRealmModel {
class func search<Thing>(needle: String) -> Results<Thing>? {
return realm()?.objects(Thing).filter("name == '\(needle)'")
}
}
let things = Thing.search("hello") // works but inferred type is Results<Object>?
The problem here is that the inferred type of things is Results<Object>?. I realize these variations can be used,
let things: Results<Thing>? = Thing.search("hello")
let things = Thing.search("hello") as Results<Thing>?
but having to specify the type every time is quite repetitive.
In my tests, using other types than Results<..>? kept the type inference intact. And this could be caused by having to specify the parent class in Self: Object (which is required because of Results).
Any help is appreciated.
This is a limitation of Swift's generics machinery. The compiler can generate a concrete signature for static func search(needle: String) -> Results<Object>? which satisfies the type constraint because Object subclasses will match this. You could probably file a bug towards bugs.swift.org because I think the Swift core team would also consider this to be a bug, if not very unexpected behavior.
However, you can modify your code to use protocol extensions to do what you want:
protocol SearchableRealmModel {}
extension SearchableRealmModel where Self: Object {
static func search(needle: String) -> Results<Self> {
return try! Realm().objects(Self).filter("name == '\(needle)'")
}
}
class Thing: Object, SearchableRealmModel {
dynamic var name = ""
}
let result = Thing.search("thing1") // => inferred as Results<Thing>
print(result.first?.name)
If you want custom implementations of search for other Realm models, you can reimplement the function there, which the compiler will prioritize over the protocol extension version:
class OtherThing: Object, SearchableRealmModel {
dynamic var id = ""
static func search(needle: String) -> Results<OtherThing> {
return try! Realm().objects(OtherThing).filter("id == '\(needle)'")
}
}

Swift: Generics and type constraints, strange behavior

I can’t figure out how to perform a cast which would let me eventually to introduce some sort of dynamism working with generics.
Next piece of code does not compile. It shows this error:
Cannot invoke 'createContainer' with an argument list of type
'(FooProtocol)' Expected an argument list of type '(T)'
protocol FooProtocol {
func doSomething()
}
class Foo : FooProtocol {
func doSomething() {}
}
class Container<T : FooProtocol> {
let someDataConformingFooProtocol : T
init(someDataConformingFooProtocol : T) {
self.someDataConformingFooProtocol = someDataConformingFooProtocol
}
}
class AllTogether {
init () {
createContainer(Foo()) //So far, so good
let foo2Mask : AnyObject = Foo()
if let foo2MaskChecked = foo2Mask as? FooProtocol {
createContainer(foo2MaskChecked)
//ERROR: Cannot invoke 'createContainer' with an argument list of type '(FooProtocol)'
//Expected an argument list of type '(T)'
}
}
func createContainer<T : FooProtocol>(data: T){
Container<T>(someDataConformingFooProtocol: data)
}
}
Is this really the expected behaviour? Because personally I can’t understand what or why the compiler is complaining about it.
What would be the appropriate cast? Without referencing to the concrete class, I mean NOT like this:
if let foo2MaskChecked = foo2Mask as? Foo
Thanks!
What it comes down to is the difference between T: FooProtocol and FooProtocol as #Hamish mentioned in his comment.
When I have a function like this:
func foo(_ i: FooProtocol)
I'm taking in an instance of type FooProtocol, so I can pass in a value that conforms to FooProtocol or its type is FooProtocol. This is similar to subclassing where you can pass both SuperClass and SubClass into SuperClass.
But if you have a function like this:
func foo<T>(_ i: T) where T: FooProtocol
I can pass in any type that conforms to FooProtocol, but because FootProtocol does not conform to itself, you can't pass that in.
So in your question, you are trying to pass in a type FooProtocol where the types must conform to FooProtocol. You could probably fix this by doing:
class Container {
let someDataConformingFooProtocol: FooProtocol
init(someDataConformingFooProtocol: FooProtocol) {
self.someDataConformingFooProtocol = someDataConformingFooProtocol
}
}
// Later
func createContainer(data: FooProtocol){
Container(someDataConformingFooProtocol: data)
}