Swift - Can I achieve mutual generics reference to classes? - swift

I want to have two classes that are bound. This means, both classes know about each other methods.
BaseController <> BaseView
Those classes are used to declare subclasses, for example
LoginController <> LoginView.
BaseController has a strong reference to its view of kind <T : BaseView>, BaseView should have a weak reference to its controller of kind <T: BaseController>
I have managed to have a generic to BaseView:
class BaseController <T: BaseScreen>
so that I do:
class LoginController : BaseController<LoginView>
that works and the controller has direct access (visibility) to the view functions and I don't need to cast every time.
However I cannot do the same on the View, because if I do
class BaseView <T : BaseController>
Then the compiler forces me to do
class BaseView <T : BaseController <BaseScreen>>
Which results in a recursion error.
Is there a way I can do that cross reference using generics? Right now I'm sticking with the view simply casting to the Controller in case it is needed.

You can use protocols and associated types instead of base classes (and implement the methods of the base type in a protocol extension):
protocol ControllerProtocol: class {
associatedtype View: ViewProtocol
var view: View { get }
}
protocol ViewProtocol: class {
associatedtype Controller: ControllerProtocol
weak var controller: Controller { get }
}
A class implementing one of the protocols has to specify a typealias to the desired related class:
class LoginController: ControllerProtocol {
typealias View = LoginView
var view: LoginView
...
}
class LoginView: ViewProtocol {
typealias Controller = LoginController
weak var controller: LoginController
...
}

Related

How can my Protocol oriented to specify ViewModel?

I have a problem from Protocol oriented ViewModels.
I have two very similar pages,so i made a baseController and baseViewModel to put shared properties and methods. And made two other controllers and viewModels for two pages.
And I made a protocol to define the properties and methods.
My baseController has var viewModel: BaseViewModelProtocol .
But my other two controller cannot use the properties and methods from their viewModel, it's says
Value of type 'BaseViewModelProtocol?' has no member ''
ViewModel1 is for Controller1, ViewModel2 is for Controller2, here is my example
protocol BaseViewModelProtocol {
var name: String { get }
func reset()
}
class BaseViewModel: BaseViewModelProtocol {
func reset() { }
}
class ViewModel1: BaseViewModel {
var score: Int = 0
func someMethods() {}
}
class ViewModel2: BaseViewModel {
var money: Int = 1000
func something() {
print("something")
}
}
class BaseViewController: UIViewController {
var viewModel: BaseViewModelProtocol?
init(viewModel: BaseViewModelProtocol) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
}
class ViewController1: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(viewModel?.score) //it is error!!!!
}
}
When i init the Controller, i Cannot use any properties and methods from ViewModel2, and the same like controller1.
class ViewController2: BaseOrderViewController {
override func viewDidLoad() {
super.viewDidLoad()
viewModel?.something(). //it is error!!!!
}
}
how to my protocol oriented viewModel?
After reading your post I feel like there is too many things that should be addressed here.
You are trying to ask something before understanding important characteristics of the Object Oriented Programmation paradigm. I recommend you to try and search information about class abstraction, composition vs inheritance and specificaly how to use the protocols in Swift.
Maybe you could start with https://cocoacasts.com/how-to-create-an-abstract-class-in-swift.
Even so, i am going to try to point out some issues in your code.
Seems like you tried to "hide" your source code by changing your classes and properties' names. The way it's done makes it harder to read, understand and introduces mistakes.
Inheriting from another class:
class ViewModel1: BaseViewModel
it's not the same as inheriting from a protocol:
class BaseViewModel: BaseViewModelProtocol
while the first one provides you a default implementation of a method that can be overrided, the second, doesn't. So it is mandatory to provide the implementation of the method in every class that inherits the protocol.
Note that the BaseViewController has a property with type BaseViewModelProtocol. That protocol doesn't have a method called "something" neither can inherit that function from another. It's within reason that the compiler shows the error.
If you want to use "something" from a Class that inherits BaseViewController, you have many ways. You could change the type of the viewModel in the BaseViewController to BaseViewModel after adding there an implementation of "something". You could also add the function to the protocol and make sure that it's implemented in all of the classes which inherit the protocol...
I hope I have helped you.

Correct way to add Generic types inheritance

I have created these classes:
class BaseViewModel<NavigatorType> {
typealias Navigator = NavigatorType
var navigator: Navigator!
}
class BaseViewController<ViewModel: BaseViewModel<Any>>: UIViewController {
typealias ViewModel = ViewModel
var viewModel: ViewModel!
}
class MyVC: BaseViewController<MyViewModel> {
}
class MyViewModel: BaseViewModel<MyNavigator> {
}
Now the problem is I receive this error on MyVC class:
'BaseViewController' requires that 'MyViewModel' inherit from
'BaseViewModel<Any>'
If I remove BaseViewModel<Any> from my BaseViewController generic parameter then the error goes. But I want to restrict view controller's generic ViewModel to inherit from BaseViewModel only.
Any idea how to do that?
The specialized type BaseViewModel<MyNavigator> is different from the specialized type BaseViewModel<Any>. I suspect you're picturing it like BaseViewModel<MyNavigator> can override/inherit from BaseViewModel<Any> since MyNavigator is a more specific Any. But Swift doesn't work that way. I don't know enough about the internals of Swift to know why it doesn't, but it doesn't!
Perhaps try making BaseViewModel a protocol instead:
protocol BaseViewModel {
associatedtype Navigator
var navigator: Navigator! { get }
}
class BaseViewController<ViewModel: BaseViewModel>: UIViewController {
var viewModel: ViewModel!
}
class MyVC: BaseViewController<MyViewModel> {
func foo() {
viewModel.navigator.bar()
print(viewModel.mySpecificProperty)
}
}
class MyViewModel: BaseViewModel {
var navigator: MyNavigator!
var mySpecificProperty: String = "Hello!"
}
class MyNavigator {
func bar() {
print("MyNavigator bar")
}
}
If the goal is to give view controllers a specific typed view model to play with, that should do it.

Overriding Swift property with descendant subclass

I'd like to override descendant property, because I want to use methods from TestViewModel class directly without casting ViewModel to TestViewModel.
As I suppose it's not possible. Could you advise some nice solution. Code and error below:
class ViewModel {
}
class View: UIView {
weak var viewModel: ViewModel!
}
class TestViewModel {
}
class TestView: View {
weak var viewModel: TestViewModel! // error here
}
Got this error:
/Users/evgeniirtishchev/Documents/Development/rdfuturesales/RDProject/View/Identification/IdentificationView.swift:12:26:
Cannot override mutable property 'viewModel' of type 'ViewModel!' with
covariant type 'IdentificationViewModel!'
Swift does not allow you to change the class type of any variables or properties.
See related question: Overriding superclass property with different type in Swift

Declare variable of type UIView conforming to a protocol in Swift 2

I need to declare a variable of type UIView which also conforms to MyProtocol:
protocol MyProtocol: class {
func foobar()
}
class MyClass {
var myView: UIView<MyProtocol>! // Error: Cannot specialize non-generic type 'UIView'
}
However I get the compiler error: Cannot specialize non-generic type 'UIView'.
I need to access methods on the variable from UIView and MyProtocol.
What is the correct variable declaration to support these requirements?
If it makes any difference, only UIView subclasses will implement the protocol. Currently I add protocol conformance via extensions.
I found this answer: https://stackoverflow.com/a/25771265/233602 but it's not clear if that answer is still the best option going in when writing in Swift 2.
Make your class a generic class as follows,
protocol MyProtocol: class {
func foobar()
}
class MyClass<T:MyProtocol where T:UIView> {
var myView: T!
}
The error above says that UIView cannot specialise to protocol MyProtocol, so, the solution here would be to make your class a generic class which takes generic parameter which conforms to MyProtocol and is subclass of UIView.
Probably the best way to solve this is to use a protocol where all UIViews conform to:
protocol UIViewType {
var view: UIView { get }
}
extension UIView: UIViewType {
var view: UIView { return self }
}
// the variable
var myView: protocol<UIViewType, MyProtocol>
Use the view property to access UIView specific functionality.
Late to the party here, but SE-0156 (adopted by Swift 4) enables class and protocol composition in type declarations without requiring (resorting to?) generics.
protocol MyProtocol: class {
func foobar()
}
class MyClass {
var myView: (UIView & MyProtocol)!
}
If it makes any difference, only UIView subclasses will implement the
protocol.
It makes all the difference! Just do this:
protocol MyProtocol: UIView {
func foobar()
}
class MyClass {
var myView: MyProtocol!
}

How to declare a swift class with a property that conforms to some protocol

Is it possible for swift to have a ViewController class, initialized from xib, has a property that is also a subclass of UIViewController and conforms to some protocol?
protocol SomeProtocol {
// Some methods
}
class ViewController: UIViewController {
// contentView is initialized from xib
#IBOutlet weak var contentView: UIView!
// I'd like to declare anotherViewController both conforms to 'SomeProtocol'
// and a subclass of UIViewController
var anotherViewController: UIViewController!
...
}
When I declare ViewController as an generic class, say class ViewController<T: UIViewController, SomeProtocol>, I get an error :
"Variable in a generic class cannot be presented in Objective-C"
So how can I fulfil it if I cannot use generic class?
Please forgive me if I misunderstood your problem, but I think what you want to do is declare a new type that inherits from UIViewController and conforms to SomeProtocol, like so:
protocol SomeProtocol { }
class VCWithSomeProtocol: UIViewController, SomeProtocol {
}
class ViewController: UIViewController {
var anotherViewController: VCWithSomeProtocol!
}
So I hope I am not misunderstanding the question as well, but it sounds like you may want a multiple-inheritance object level mixin such as:
let myVC: ViewController, SomeProtocol
Unfortunately, Swift does not support this. However, there is a somewhat awkward work-around that may serve your purposes.
struct VCWithSomeProtocol {
let protocol: SomeProtocol
let viewController: UIViewController
init<T: UIViewController>(vc: T) where T: SomeProtocol {
self.protocol = vc
self.viewController = vc
}
}
Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.
For Instance:
class SomeClass {
let mySpecialViewController: VCWithSomeProtocol
init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
}
}
Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.
Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.
Hope this helps!