Overriding variable property with different type - ios

I have a variable 'children', which is just a list of participants. Can one please explain how can I override a variable with one type to a property with another type.
Here is a code:
class ParticipantsListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var participantsTableView: UITableView!
// Error on next line: Property 'children' with type '[Child]?' cannot override a property with type '[UIViewController]'
var children: [Child]?
var trackingHelper = TrackingHelper()
var modelHelper = ModelHelper.shared
}

The problem is very simple. You can't do that. UIViewController, the class you're inheriting from, has this property under lock and key. You'll need to create yourself a new solution depending on what you're trying to achieve:
Condition A: Child is a subclass of UIViewController
In this case, you want a way to make the child view controllers of ParticipantsListViewController always conform to Child. One way to do this would be the following computed property:
var listChildren: [Child] {
return children.filter { $0 is Child }
}
Condition B: Child is NOT a subclass of UIViewController
You're trying to override something that the system needs to be there. Things in the children array have to be instances or subclasses of UIViewController. It's strict.
Your solution here is easy. Name the property differently and get rid of the override. Sure, it won't have the nicest, simplest name children, but that's the way it goes.

No, you can't have children property with your custom type since property with the same name is already property of UIViewController (see docs) which is superclass of your view controller so your subclass has this property too.
So, you have to use other name for your variable.

You can accomplish this using protocol conformance for your Child view controllers as demonstrated here:
import UIKit
protocol ChildProtocol { }
class SomeViewController: UIViewController, ChildProtocol { }
class ParticipantsListViewController: UIViewController {
override var children: [UIViewController] {
return self.children.filter { viewController in viewController is ChildProtocol }
}
}
So long as the view controllers that you wish to be a Child conform to ChildProtocol, then they will be returned via the children override.

Related

iOS: MVVM-C ViewController super class

I have MVVM-C arch. Each UIViewController has a ViewModel and CoordinatorDelegate to notify the Coordinator when navigation needs to be performed. The code that create the VC repeat itself, and I though it would be great to create a super class to unify all static funcs that create the VC. Like this:
import UIKit
class MVVMCViewController: UIViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
static func initVC(storyboard: Storyboard,
coordinatorDelegate: CoordinatorDelegate?,
viewModel: Modelling?) -> Self {
let viewController = Self.instantiate(in: storyboard)
viewController.coordinatorDelegate = coordinatorDelegate
viewController.viewModel = viewModel
return viewController
}
}
All CoordinatorDelegateProtocols will inherit from CoordinatorDelegate and all ViewModels will be inheriting from Modelling
But the subclassing does not work smoothly.
Any ideas?
Hi this model wouldn't work fine.
MVVMCViewController has hardcoded protocols as variable type, so You should have the same in your childVC.
To make it work as u want MVVMCViewController show be generic (but can be a lot of issues with it), like
class MVVMCViewController<T: Modelling, U: CoordinatorDelegate>: UIViewController {
weak var coordinatorDelegate: U?
var viewModel: T?
}
or add just casted properties to ConnectViewController
class ConnectViewController: MVVMCViewController {
weak var coordinatorDelegate: CoordinatorDelegate?
var viewModel: Modelling?
var currentDelegate: ConnectViewControllerCoordinatorDelegate? {
coordinatorDelegate as? ConnectViewControllerCoordinatorDelegate
}
var currentVM: ConnectViewModel? {
viewModel as? ConnectViewModel
}
}
Your superclass MVVMCViewController defines two properties coordinatorDelegate and viewModel. If you just need to access them in your child class ConnectViewController, just access it normally. You don't need to define it again.
Also, in your parent class, you have weak var coordinatorDelegate: CoordinatorDelegate?. But in your child class (ConnectViewController), you redeclare the property with a different type (ConnectViewControllerCoordinatorDelegate?). That is illegal, even if it is a subclass of CoordinatorDelegate.
Hence, either
Rename the property in child class to avoid the conflict
Keep the name and the type, but add an override keyword for the property if you plan to add additional functionality in your child class
Do not declare the property again at all in your child class if you don't need to add additional functionality to it. Just access it directly.
Refer to how inheritance works in Swift over here: https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html

How to use foreign variables in classes

I have two .swift files so I want one of them to modify the text of an IBoutlet label that is the other class.
Class 1:
class class1: UIViewController {
#IBOutlet var label1: UILabel!
}
Class 2:
class class2: SKScene {
var cool_variable = 1
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
}
by doing this I'm getting the following error message:
Instance member "label1" cannot be used on type "class2"
Thanks in advance!
The distinction and relationship between classes and instances is absolutely crucial to object-oriented programming. Thus, consider your proposed code:
func coolFunc () {
class1.label1.text = "\(cool_variable)"
}
The problem here is that class1 is the name of a class. (That would be more obvious if you just, please, follow the elementary rule that class names start with a capital letter.) But label1 is an instance property. Thus what you need here is the name of an instance of the class1 class — presumably a reference to the actual existing instance that is already part of the view hierarchy.
You never create an instance of class1 in class2 to access the variables.
My guess is that you are using Storyboards. In this case you wouldn't want to create an instance of class1. Instead you would use delegation (This would also be a good idea if you are not using Storyboards).
Delegation can be a complicated topic, so I will try to keep this simple.
First, you start with a protocol. Usually you name it something like <CLASS-NAME>DataSource, so you would do something like:
protocol class2DataSource: class {}
The class keyword is required for delegation protocols.
Then you would add the methods to it that you want called in other classes when you call a method in the class the protocol delegates for, so, for example, willCoolFunc:
protocol class2DataSource: class {
func willCoolFunc(with variable: Int)
}
You have the parameter so you can access the variable cool_variable as you are trying to.
Next, you need to create a a variable in class2 that is of type class2DataSource:
weak var dataSource: class2DataSource?
Make sure the variable is weak and an optional.
Next, call the method, you would do it in coolFunc:
func coolFunc () {
dataSource?.willCoolFunc(with: cool_variable)
}
Now you, to access cool_variable when the function is called, you need to implement class2DataSource on class1. Create an extension of class1 that implements class2DataSource and add the function willCoolFunc:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
}
}
Now you can access the variable cool_variable in class1! The reason why is because when you call class2.coolFunc(), the willCoolFunc method is called with cool_variable passed in. Any class that implements the class2DataSource can access cool_variable with the willCoolFunc method.
To finish of the method, here is what the extension would look like:
extension class1: class2DataSource {
func willCoolFunc(with variable: Int) {
self.label1.text = "\(variable)"
}
}
We are almost done, but not quite. We still have to set the class1 as the data source for class2DataSource. To do this, I will reference Nikolay Mamaev from this post:
Go to the Interface Builder.
Type "Object" in the search text field of the Objects Library and drag an 'Object' to your view controller containing that connects to
class1 (i.e. do the same as you add any view or view controller to
storyboard scene, with the exception you add not a view or view
controller but an abstract object).
In the left-side 'Scenes' panel of your storyboard, highlight just added Object; in right-side panel go to the 'Identity Inspector' and
type class2DataSource instead of pre-defined NSObject. In left side
panel ('Scenes'), 'Object' will be renamed to 'Class2 Data Source'
automatically.
In the left-side 'Scenes' panel of your storyboard, control-drag from your UIView [*not the controller*] to the 'Class2 Data Source';
in pop-up appeared, select dataSource outlet.
There! You now set class1's label1's text to the value of cool_variable when you call class2.coolFunc()!
I do not know the exact problem you're trying to solve, but I'm just going to consider the part that you want to access the variable in class1 in class2. There are two basic ways to go about this, one is Inheritance and the other one is by creating an object. These are basic Object Oriented Programming concepts which you need to be familiar with.
Swift does not support multiple inheritance. So that rules out inheritance for solving you problem, since you are inheriting two classes SKScene and UIViewController.
Create an object in the class1 and call the function coolFunc
class class1: UIViewController {
#IBOutlet var label1: UILabel!
func modifyLabel(){
let someObject = class2()
someObject.coolFunc()
}
}
Of course this solution might not be the one you're looking for. You will have to explain more about the problem you're facing if you need a solution that will work for you.

Swift property conforming with multiple protocols

I have custom UIView (CustomView) conforming to two different protocols
protocol ResizableDelegate: class {
func view(view:UIView, didChangeHeight difference:CGFloat)
}
protocol Resizable: class {
var delegate:ResizableDelegate? { set get }
}
protocol TappableDelegate: class {
func viewDidTap(view:UIView)
}
protocol Tappable {
var delegate:TappableDelegate? { set get }
}
And I need to have a property in my CustomView class named delegate and conforming to these two protocols at the same time. I read Types conforming to multiple protocols in swift but that is not solving my problem.
I created this protocol
protocol CustomViewDelegate: ResizableDelegate, TappableDelegate {}
And then make my CustomView
class CustomView : UIView, Resizable, Tappable {
var delegate:CustomViewDelegate?
}
But that is causing me to get a message
Type 'CustomView' does not conform to protocol 'Resizable'
I don't want to have:
class CustomView : UIView, Resizable, Tappable {
var resizableDelegate:ResizableDelegate?
var TappableDelegate:TappableDelegate?
}
Is there any way two have only one delegate property that conforms to these two protocols at the same time? Im using swift 2.0, Xcode 7.
I guess you don't really need to declare Resizable and Tappable protocols. All you need is to delegate from your custom view to some other object, which confirms to both ResizableDelegate and TappableDelegate, right? If so, this should work for you:
protocol ResizableDelegate: class {
func view(view:UIView, didChangeHeight difference:CGFloat)
}
protocol TappableDelegate: class {
func viewDidTap(view:UIView)
}
class CustomView : UIView {
var delegate: (ResizableDelegate, TappableDelegate)?
}
Although you can leave things as is, I would strongly recommend changing the required properties to "tappableDelegate" and "resizableDelegate" thus having two separate properties in your View subclass.
Your specific use case may require adherence to both, but having the same naming means you would not be able to have different delegates.

Swift Property that conforms to a Protocol and Class

#property (strong, nonatomic) UIViewController<UITableViewDelegate> *thing;
I want to implement a property like in this Objective-C code in Swift. So here is what I've tried:
class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController {
var thing: T!
}
This compiles. My problem comes when I add properties from the storyboard. The #IBOutlet tag generates an compiler error.
class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController {
#IBOutlet weak var anotherThing: UILabel! // error
var thing: T!
}
The error:
Variable in a generic class cannot be represented in Objective-C
Am I implementing this right? What can I do to fix or get around this error?
EDIT:
Swift 4 finally has a solution for this problem. See my updated answer.
Update for Swift 4
Swift 4 has added support for representing a type as a class that conforms to a protocol. The syntax is Class & Protocol. Here is some example code using this concept from "What's New in Swift" (session 402 from WWDC 2017):
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }
// Example function to generically shake some control elements
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.isEnabled {
control.shake()
}
}
As of Swift 3, this method causes problems because you can't pass in the correct types. If you try to pass in [UIControl], it doesn't have the shake method. If you try to pass in [UIButton], then the code compiles, but you can't pass in any UISliders. If you pass in [Shakeable], then you can't check control.state, because Shakeable doesn't have that. Swift 4 finally addressed the topic.
Old Answer
I am getting around this problem for the time being with the following code:
// This class is used to replace the UIViewController<UITableViewDelegate>
// declaration in Objective-C
class ConformingClass: UIViewController, UITableViewDelegate {}
class AClass: UIViewController {
#IBOutlet weak var anotherThing: UILabel!
var thing: ConformingClass!
}
This seems hackish to me. If any of the delegate methods were required, then I would have to implement those methods in ConformingClass (which I do NOT want to do) and override them in a subclass.
I have posted this answer in case anyone else comes across this problem and my solution helps them, but I am not happy with the solution. If anyone posts a better solution, I will accept their answer.
It's not the ideal solution, but you can use a generic function instead of a generic class, like this:
class AClass: UIViewController {
#IBOutlet weak var anotherThing: UILabel!
private var thing: UIViewController?
func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) {
thing = delegate
}
}
I came across the same issue, and also tried the generic approach. Eventually the generic approach broke the entire design.
After re-thinking about this issue, I found that a protocol which cannot be used to fully specify a type (in other words, must come with additional type information such as a class type) is unlikely to be a complete one. Moreover, although the Objc style of declaring ClassType<ProtocolType> comes handy, it disregards the benefit of abstraction provided by protocol because such protocol does not really raise the abstraction level. Further, if such declaration appears at multiple places, it has to be duplicated. Even worse, if multiple declarations of such type are interrelated (possibly a single object will be passed around them ), the programme becomes fragile and hard to maintain because later if the declaration at one place needs to be changed, all the related declarations have to be changed as well.
Solution
If the use case of a property involves both a protocol (say ProtocolX) and some aspects of a class (say ClassX), the following approach could be taken into account:
Declare an additional protocol that inherits from ProtocolX with the added method/property requirements which ClassX automatically satisfy. Like the example below, a method and a property are the additional requirements, both of which UIViewController automatically satisfy.
protocol CustomTableViewDelegate: UITableViewDelegate {
var navigationController: UINavigationController? { get }
func performSegueWithIdentifier(identifier: String, sender: AnyObject?)
}
Declare an additional protocol that inherits from ProtocolX with an additional read-only property of the type ClassX. Not only does this approach allow the use of ClassX in its entirety, but also exhibits the flexibility of not requiring an implementation to subclass ClassX. For example:
protocol CustomTableViewDelegate: UITableViewDelegate {
var viewController: UIViewController { get }
}
// Implementation A
class CustomViewController: UIViewController, UITableViewDelegate {
var viewController: UIViewController { return self }
... // Other important implementation
}
// Implementation B
class CustomClass: UITableViewDelegate {
private var _aViewControllerRef: UIViewController // Could come from anywhere e.g. initializer
var viewController: UIViewController { return _aViewControllerRef }
... // UITableViewDelegate methods implementation
}
PS. The snippet above are for demonstration only, mixing UIViewController and UITableViewDelegate together is not recommended.
Edit for Swift 2+: Thanks for #Shaps's comment, the following could be added to save having to implement the desired property everywhere.
extension CustomTableViewDelegate where Self: UIViewController {
var viewController: UIViewController { return self }
}
you can declare a delegate in Swift like this:
weak var delegate : UITableViewDelegate?
It will work with even hybrid(Objective-c and swift) project. Delegate should be optional & weak because its availability is not guaranteed and weak does not create retain cycle.
You are getting that error because there are no generics in Objective-C and it will not allow you to add #IBOutlet property.
Edit: 1. Forcing a type on delegate
To force that delegate is always a UIViewController you can implement the custom setter and throw exception when its not a UIViewController.
weak var _delegate : UITableViewDelegate? //stored property
var delegate : UITableViewDelegate? {
set {
if newValue! is UIViewController {
_delegate = newValue
} else {
NSException(name: "Inavlid delegate type", reason: "Delegate must be a UIViewController", userInfo: nil).raise()
}
}
get {
return _delegate
}
}

Swift - Same protocol for two different classes

I want to use the same protocol for two different classes. It is for two UIStoryboardSegue classes, the normal one and the unwind segue. In my first class GameSegue.swift, I've declared this protocol
#objc protocol ViewControllerWithBackgroundImage {
var backgroundImage: UIImageView { set get }
}
I use this protocol to have access to the ViewControllers property backgroundImage. In the first class GameSegue.swift, the normal segue, the backgroundImage animates 10 px up. So in the second class GameSegueUnwind.swift, I want to do the same thing backwards, move the background 10 pxdown. But to get access to the backgroundImage property I need this protocol. Therefore it would be useful, to not declare another protocol, but instead use the same.
Any idea how this is possible?
In the second class just declare a new delegate variable
class GameSegueUnwind {
var secondDelegate: ViewControllerWithBackgroundImage?
}
and you will be able to access the function in any other class that conforms to the protocol. Of course, in the conforming class remember to declare it has the delegate handler in the prepare for segue method
destinatonViewController.secondDelegate = self

Resources