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
Related
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.
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.
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
...
}
I have a (swift) class which has a weak pointer to a delegate like this:
import UIKit
#objc public protocol DRSlidingPanelViewControllerDelegate : class {
optional func didSlidePanel(panelHidden : Bool , sender : DRSlidingPanelViewController) -> Void
}
public class DRSlidingPanelViewController: UIViewController {
public weak var delegate : DRSlidingPanelViewControllerDelegate?
///other stuff...
}
Now i make a subclass with another protocol which extends the first, and i want to alter the inherited 'delegate' property
#objc public protocol DRTableViewControllerDelegate : DRSlidingPanelViewControllerDelegate {
optional func someFunction(sender : DRTableViewController) -> Void
}
public class DRTableViewController: DRSlidingPanelViewController {
// public weak var delegate : DRTableViewControllerDelegate?
}
^ this (re)declaration of delegate in the subclass gives me 3 errors when I uncomment it.
Property 'delegate' with type 'DRTableViewControllerDelegate?' (aka 'Optional') cannot override a property with type 'DRSlidingPanelViewControllerDelegate?' (aka 'Optional')
Getter for 'delegate' with Objective-C selector 'delegate' conflicts with getter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Setter for 'delegate' with Objective-C selector 'setDelegate:' conflicts with setter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Now i understand the nature of these errors, and that they are different facets of the one error (attempting to change the 'type' on the delegate pointer.) Can anybody give me a clue how to do this? It obviously can be done, look at how UITableView alters the delegate pointer which it inherits from UIScrollView. In objC I would get a warning which could be silenced with an #dynamic.
Thanks and best regards
edit / addition
Thanks Matt, I do see this previous question, but unfortunately it is closed and I would personally not accept that answer as the definitive answer because it is a compromise.
If I right click on UITableView in xCode and 'jump to definition' I see this
#available(iOS 2.0, *)
public class UITableView : UIScrollView, NSCoding {
public init(frame: CGRect, style: UITableViewStyle) // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
public init?(coder aDecoder: NSCoder)
public var style: UITableViewStyle { get }
weak public var dataSource: UITableViewDataSource?
**weak public var delegate: UITableViewDelegate?**
//plenty other stuff..
}
So I respectfully submit that there definitely is a way to do this.
I would say that as things stand you can't do it. This is disappointing to say the least. You'll just have to call the delegate variable in the subclass something else.
So, this is legal, but of course it totally fails to meet your requirements:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2?
}
If you really hate the multiplication of storage you can make the delegate2 a computed variable that accesses the inherited delegate:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2? {
set {
super.delegate = newValue
}
get {
return super.delegate as? P2
}
}
}
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!
}