class ArcaneCardVC: UIViewController {
var currentCard: ArcaneCardView?
}
class PostVC: ArcaneCardVC {
override var currentCard: PostCard?
// <===== This is what I want to do but cant
}
class ArcaneCardView: UIView {
}
class PostCard: ArcaneCardView {
}
Here is the error I get:
Cannot override mutable property 'currentCard' of type 'ArcaneCardView?' with covariant type 'PostCard?'
The other solution is explicitly doing this in code everytime I use currentCard:
var card = currentCard as! PostCard
When you override a variable, you can't change it's type. Why not? Well, suppose that you are able to do that, then the following scenario would be possible:
var A: PostVC = PostVC() // some initialization
var B: ArcaneCardVC = A // this is a valid state since `PostVC` is a subclass of `ArcaneCardVC`
What should be the type of B.currentCard? Hmm, this is a complicated question. You can answer that its type should be PostCard. Ok, lets add other classes to the party:
class OtherCard: ArcaneCardView {
}
class OtherVC: ArcaneCardVC {
override var currentCard: OtherCard?
}
Considerer now the following code:
var A: ArcaneCardVC = PostVC()
var B: ArcaneCardVC = OtherVC()
A.currentCard = B.currentCard // something will crash here!!!
To avoid this kind of behavior, you can't change the type of a property when you are subclassing.
The correct way to do it is the way you're doing with currentCard as! PostCard.
Another option would be to use a property getter like
// inside PostVC
// Note the camel case on the 'C' makes it a different variable that the super class
var CurrentCard: PostCard {
get { return self.currentCard as! PostCard }
}
Then you'd use self.CurrentCard instead of self.currentCard as! PostCard
Related
I am new to Swift and I'm facing a problem.
My problem is quite simple:
I have a simple class with some object in it (declared in their own file) :
class Outcome : NSObject {
var foo : SomeClass?
var doo : OtherClass?
var roo: AnotherClass?
}
What I want to do is when an object of the class is set, set all the other object to nil, so I used the didSet feature and add a resetData method that is supposed to set all the object to nil, except the one I just set.
The class looks like this :
class Outcome : NSObject {
var foo : SomeClass? {
didSet { self.resetData(exeption: foo!) }
var doo : OtherClass? {
didSet { self.resetData(exeption: doo!) }
}
var roo: AnotherClass? {
didSet { self.resetData(exeption: roo!) }
}
func resetData (exeption: AnyObject) {
var allObjects = NSArray(array: [self.foo ?? NSNull(), self.doo ?? NSNull(), self.roo ?? NSNull()])
for var anObject in (allObjects as NSArray as! [NSObject?]) {
if exeption.isEqual(anObject) == false {
anObject = nil
}
}
}
}
The idea of the resetData method is to set all the objects to nil except the exception (which is the object I just set in my view controller)
The code is working and the instruction anObject = nil is executed, but the problem is that when I set a set a beakpoint I can clearly see that the object is not nil, so this does not do what I want.
I think it is a question of reference, I do not really get it, is it possible to set an object to nil ? Is there a better way to do this ?
As mentioned in the comments, your loop does not modify the class
properties at all, only the anObject loop variable.
If the intention is to define a type which holds either an instance
of SomeClass or an instance of OtherClass or an instance
of AnotherClass then an enum would be a better choice:
enum Outcome {
case foo(SomeClass)
case doo(OtherClass)
case roo(AnotherClass)
}
Usage example:
var outCome = Outcome.foo(SomeClass())
print(outCome) // foo(<SomeClass: 0x1009000b0>)
// ...
outCome = .doo(OtherClass())
print(outCome) // doo(<OtherClass: 0x100d00540>)
protocol BasePresenterProtocol : class {}
protocol DashboardPresenterProtocol : BasePresenterProtocol {}
final class DashboardPresenter {
weak var view: DashboardPresenterProtocol?
init() {
self.view = DashboardViewController()
}
func test() {
print("Hello")
}
}
extension DashboardPresenter: DashboardViewProtocol { }
protocol BaseViewProtocol : class {
weak var view: BasePresenterProtocol? { get set }
}
protocol DashboardViewProtocol : BaseViewProtocol {
}
class DashboardViewController {
}
extension DashboardViewController: DashboardPresenterProtocol { }
In the above code, I get an error at following line
extension DashboardPresenter: DashboardViewProtocol { }
that, DashboardPresenter doesn't confirm to protocol DashboardViewProtocol, but I have declared weak var view: DashboardPresenterProtocol? in DashboardPresenter . Although I have declared
Why am I getting this error ? Please let me know what I am doing wrong in this code.
You cannot implement a read-write property requirement of type BasePresenterProtocol? with a property of type DashboardPresenterProtocol?.
Consider what would happen if this were possible, and you upcast an instance of DashboardPresenter to DashboardViewProtocol. You would be able to assign anything that conforms to BasePresenterProtocol to a property of type DashboardPresenterProtocol? – which would be illegal.
For this reason, a read-write property requirement has to be invariant (although it's worth noting that a readable-only property requirement should be able to be covariant – but this currently isn't supported).
I've seen some discussions about this problem, but have not read a satisfactory explanation. Can anybody tell me why this does not work?
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child : Parent<Int> {}
let c = Child(data: 4)
The last line gives the error:
'Child' cannot be constructed because it has no accessible initializers
Do I really need to implement the initializer just to call super?
Edit:
To give a bit of context, the real code looks closer to the below. I have an Action class which uses generics, because I have another bit of code which can chain actions together and I want to use Swift's type safety to ensure that actions can be chained. Then I have a bunch of subclasses classes (e.g. CustomAction). I am looking for a way to avoid overriding the init method in each of the subclasses. Or alternatively, I want to understand why that's not possible.
class Action<Input, Output> {
var cachedOutput:Output?
init(cachedOutput:Output?) {
self.cachedOutput = cachedOutput
}
}
protocol CustomInput {}
protocol CustomOutput {}
class CustomAction : Action<CustomInput, CustomOutput> {
}
yes, you really need to override init method ..
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<T> : Parent<T> {
override init(data: T) {
super.init(data: data)
}
}
let c = Child(data: 4) // Child<Int>
let c2 = Child(data: "alfa") // Child<String>
what are the errors ...
// what is the type T ? it is undeclared!
class Child2: Parent2<T> {}
// how to specialize non-generic type Parent ? how to create it?
// i need an initializer in class Child3 ... Hm ...
class Child3: Parent<Int> {}
// cannot specialize non-generic type 'Parent'
class Child3: Parent<Int> {
override init(data: Int) {
super.init(data: data)
}
}
// So, Child3 must be of the same specialized type as Parent!!
that is terrible, isn't it? so look at my final example !
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child<Double> : Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4) // Child<Int> !!!!!
let d = Child(data: true) // Child<Bool> !!!
in your case it works like
class Parent<T> {
var data:T
init(data:T) {
self.data = data
}
}
class Child: Parent<String> {
init(data: Double) {
super.init(data: "\(data)")
}
}
let c = Child(data: 4)
print(c.dynamicType) // Child :-)
This now works in Swift 3. My original example now compiles. There is no mention of this in the Swift 3 Language changes, so I can only assume that this was a bug.
I ran into a problem when I was trying to "translate" some Objective-C code to Swift. I define Garage and Car in CoreData. Garage has a relationship to Car called cars. I have a masterviewcontroller to display "Garage" class and detailviewcontroller to display a NSArray of "car" class. Here is my code in Objective-C. I want to let cars = allobjects when it is nil; otherwise just return it.
#property (nonatomic, strong) NSArray* cars;
- (NSArray*) cars {
if (_cars == nil) {
_cars = self.garage.cars.allObjects;
}
return _cars;
}
However, in Swift, it does not have a underscore instance for property, and I cannot let cars == nil since "==" cannot be applied to operands of type [Car]. I tried to use the following code, but it gave me two errors: "attempting to access 'cars' within its own getter" and "cannot assign a value of type '[AnyObject]?' to a value of type '[Car]'"
var garage : Garage?
var cars : [Car] {
if let a = cars {
get {
cars = self.garage?.cars.allObjects
}
}
Any help is appreciated.
UPDATE/////////////////////////////////////////////
Here is the method I used to solve my problem.
private var _cars: [Car]?
var cars: [Car]?
{
get {
if _cars == nil {
_cars = self.garage?.cars.allObjects as? [Car]
}
return _cars
}
set {
_cars = cars
}
}
Thank you for all the help.
I might suggest just having a computed property:
var cars : [Car]? {
return garage?.cars.allObjects
}
And, remember to make cars variable and optional array.
Looks like you need a lazy property:
lazy var cars: [Car]? = self.garage?.cars.allObjects
Yes, you need a lazy var.
Your code will be (Swift 2.3):
lazy var cars: [Car]? = { [unowned self] in
return self.garage?.cars.allObjects
}()
The trick are the { and the [unowned self]
Lazy var in sintax instance the value just one time and keep it, maybe it's what you want.
Maybe you just need a wrapper to get each time the value of self.garage?.cars.allObjects in which case you need a computed var, not a lazy var with a static value
var cars: [Car]? {
get {
return self.garage?.cars.allObjects
}
}
In Swift, you would generally replace properties with variables (var); if a property can be set in you init method and stays unchanged after that, you would use (let).
In your case, you have your own "get" method. In Swift, when you have a getter or setter method, there is no backing variable. So what you do, you create one. Typically as a private variable with a leading underscore.
You should think about how you reset the variable to nil. Having a setter is not a good idea, because you only want to allow setting the variable to nil and nothing else. If it is only set to nil inside the class itself, you can set the variable with underscore to nil. Otherwise you might add a function like "resetCars".
And note that if you have a "get" method, but none of "set", "willSet" and "didSet", you can just write the set code without any getter. So a typical usage would be:
private var _cars: [Car]?
func resetCars () -> Void { _cars = nil } // To allow resetting outside the class
var cars: [Car] {
if _cars == nil {
_cars = cleverlyCalculatedNonNilValue()
}
return _cars
}
When I create two classes like so:
class Example
{
}
class OtherExample
{
init() {
println("created")
}
}
var instance = Example()
var otherInstance = OtherExample()
Both seem to create usable instances, so I'm wondering what the difference is in Swift if you don't provide an init method, and yet you initialise as above?
I did think it probably called the superclass init automatically, however since both of these objects don't inherit from NSObject, they don't have super classes do they?!
Also is there a need to class super.init() in the otherExample?
You only need super.init() if your class inherits from another class.
class Example {
func sayHi() {
print("hi")
}
}
class OtherExample: Example {
override init() {
super.init()
print("created")
}
}
var instance = Example()
instance.sayHi()
// hi
var otherInstance = OtherExample()
otherInstance.sayHi()
// created
// hi
class example{
var example:Int = 0
}
class anotherExample{
init(example:Int){
self.example = example
}
var example:Int
}
example()
anotherExample(0)
You can have not initialized variables in a class with an initializer, if you don’t have any initializer you will need to set a value in the class
Both will be able to change their example value but only anotherExample will have a value that can be set.
In my test playground I was unable to use super.init() in the classes (since they don’t have any superclasses to init to/from)