Wilfred
Wilfred

Reputation: 197

Swift generic inheritance

I have one base view controller and some child view controllers. I am having difficulty to pass the view model created in children view controller (CommentViewController) to it's parent view controller (FeedBaseViewController) to access.

class BaseViewController: UIViewController {
    ...
}

class FeedBaseViewController: BaseViewController {
    var viewModel1 = FeedBaseViewModel<BaseObject>()
    ...
}

class CommentViewController: FeedBaseViewController {
    var viewModel2: CommentViewModel<CommentObject>

    init() {
        viewModel2 = CommentViewModel()

        /* This is where I have issue, and I tried a few approaches. And here are the 2 of them.*/
        // Method 1:
        // viewModel1 = viewModel2
        // Error: Cannot assign value of type 'CommentViewModel<CommentObject>' to type 'FeedBaseViewModel<BaseObject>'

        // Method 2:
        // viewModel1 = viewModel2 as FeedBaseViewModel<BaseObject>
        // Error: Cannot convert value of type 'CommentViewModel<CommentObject>' to type 'FeedBaseViewModel<BaseObject>' in coercion

        ...

    }
    ...
}

My view models for the View Controller

class FeedBaseViewModel<Item: BaseObject> {
}

class CommentViewModel<T: CommentObject>: FeedBaseViewModel<CommentObject> {
}

Here are the object classes, the BaseObject class is the only Objective-C class here, and a CommentObject swift class which inherit from BaseObject.

@interface BaseObject : NSObject <NSCoding>
@end

class CommentObject: BaseObject {
}

I am not sure if it is possible to do, or it is only syntax issue. Please advice, thank you!

Edited(2017-09-11):

I think I could provide more information of what I would like to achieve, and hope it helps.

FeedBaseViewModel stores and manipulate an array of items with type BaseObject. FeedBaseViewController have UI handling actions such as "filtering", "sorting", and ask FeedBaseViewModel to do the job.

For the Comment part, I will have a CommentObject array with basic manipulation and some specific manipulation. This is why CommentViewModel comes in, CommentViewModel inherit FeedBaseViewModel so that basic manipulation can be handled by the parent, and it will do the specific manipulation itself.

Similar in the view controller part, CommentViewController will inherit FeedBaseViewController, so FeedBaseViewController can do the basic handling while CommentViewController itself can provide additional functions.

Upvotes: 2

Views: 940

Answers (1)

luk2302
luk2302

Reputation: 57124

I do not think that is possible.

Your problem boils down to:

let m = FeedBaseViewModel<CommentObject>()
let k : FeedBaseViewModel<BaseObject> = m

That is not allowed. As to why: assume that FeedBaseViewModel contains a member of its generic type and offers a set function for that member. If you now would be allowed to write the second line, then call set using a BasObject instance that would violate the generic constraint of m because they are still the same object instance with different generic constraints, a BaseObject instance is not necessarily an instance of CommentObject.

A solution in your case should be to move the generics up a little bit more:

class FeedBaseViewController<T : BaseObject>: BaseViewController {

    var viewModel1: FeedBaseViewModel<T>

    required init?(coder aDecoder: NSCoder) {
        viewModel1 = FeedBaseViewModel<T>()
        super.init(coder: aDecoder)
    }
}

class CommentViewController: FeedBaseViewController<CommentObject> {
    var viewModel2: CommentViewModel<CommentObject>

    required init?(coder aDecoder: NSCoder) {
        viewModel2 = CommentViewModel<CommentObject>()
        super.init(coder: aDecoder)
        viewModel1 = viewModel2 
        // now this assignment is allowed because viewModel1 and viewModel2 have the super type FeedBaseViewModel<CommentObject>
    }
}

Not sure if that solution works in your situation though.

Upvotes: 2

Related Questions