Subclassing NSObject in Swift - Best Practice with Initializers - ios

Here is the layout of an example Class, can someone guide me on what's best practice when creating a subclass of NSObject?
class MyClass: NSObject {
var someProperty: NSString! = nil
override init() {
self.someProperty = "John"
super.init()
}
init(fromString string: NSString) {
self.someProperty = string
super.init()
}
}
Is this correct, am I following best practice here?
I wonder if I'm correctly setting up the initializers (one that sets the string to a default, and one which I can pass in a string)?
Should I call super.init() at the end of each of the initializers?
Should my more specific (the one that takes a string) initializer simply call self.init() at the end rather than super.init()?
What is the right way to set up the initializers in Swift when subclassing NSObject? - and how should I call the super init ?
This question (albeit in Objective C) suggests you should have an init, which you always call and simply set the properties in more specific inits: Objective-C Multiple Initialisers

I'm not Swift ninja but I would write MyClass as:
class MyClass: NSObject {
var someProperty: NSString // no need (!). It will be initialised from controller
init(fromString string: NSString) {
self.someProperty = string
super.init() // can actually be omitted in this example because will happen automatically.
}
convenience override init() {
self.init(fromString:"John") // calls above mentioned controller with default name
}
}
See the initialization section of the documentation

If someProperty can be nil, then I think you want to define the property as:
var someProperty: NSString?
This also eliminates the need for a custom initializer (at least, for this property), since the property doesn't require a value at initialization time.

In complement to the answers, a good idea is to call super.init() before other statements. I think it's a stronger requirement in Swift because allocations are implicit.

Related

Initialising properties in a subclass?

I have a subclass of a subclass as i require a variation on a varients behaviour based on a base class.
The issue im facing is that this new subclass requires some custom values that need to be initialised in an init method.
The situation is:
I have a value e.g. let time: Dynamic<String?> where I have to use let due to the binding method
and so i need to provide a value in an init in order to compile...
but as the class signature looks like this: class NewViewModel: DurationViewModel<Model>
the superclass is also class DurationViewModel<T: Model>: BaseViewModel<T>
I dont seem to be able to use convenience init or init with a call to super
Is there a correct way to achieve this so i can initialise these let constants in this subclass
I'm not sure I understand. Does this not work for you?
class Parent {
let someVar: String
init() {
someVar = "Some value"
}
}
class Child: Parent {
let someOtherVar: String
override init() {
someOtherVar = "Some other value"
super.init()
}
}

access property from another class

I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint

Override var conforming to a protocol with a var conforming to a child of the overridden var protocol

This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd

Swift error: "class cannot be constructed because it has no accessible initializers"

Xcode is giving me this error for my Swift code:
'myColor' cannot be constructed because it has no accessible initializers
import Foundation
protocol Prototype {
func Clone<T>() -> T
}
class myColor: Prototype {
var red: Int?
var green: Int?
var blue: Int?
init () {}
func Clone<myColor>() -> myColor {
let newColor = myColor()
newColor.red = self.red
newColor.green = self.green
newColor.blue = self.blue
return newColor
}
}
The error is on line:
let newColor = myColor()
Type 'myColor' has no member 'init'
Even if you set your framework to public, you still need to declare all classes you want to make accessible as 'public'. Same goes for your init method.
public init() {
}
Did the trick for me.
First, classes have leading caps. Methods have leading lowercase. You mean MyColor and clone().
You're confusing the compiler at this point:
func Clone<myColor>() -> myColor {
It thinks you mean that myColor is a type variable that is shadowing the class name. So when you get to myColor(), it's basically the same thing as T(), which has no trivial constructor.
If you fix this stuff up, you'll find that the correct error is
Type 'MyColor' does not conform to protocol 'Prototype'
That error is a completely different problem. See Protocol func returning Self for an explanation of how to implement a copy protocol. You also may be interested in the followup: Swift protocol and return types on global functions.
You will hit this error if you attempt to add a new property to a subclass without adding new initializers.
'SomeSubclass' cannot be constructed because it has no accessible initializers
Suddenly none of the existing superclass's initializers will be valid, as the new variable needs to be set within an init() method on the subclass (which doesn't yet exist).
class SomeSubclass: UIViewController { // Class 'SomeSubclass' has no initializers
var date: Date
class func `for`(_ date: Date) -> SomeSubclass {
let someSubclass = SomeSubclass() // 'SomeSubclass' cannot be constructed because it has no accessible initializers
someSubclass.date = date
return someSubclass
}
}
A fix for most UIViewController situations, as is commonly used for IBOutlets, is to declare the subclass's properties as optionals or implicitly unwrapped optionals, which will be valid and initialized to nil when using the existing superclass init methods. Then set the value immediately after initialization.
var date: Date?
// - or - //
var date: Date!
Seems generic generates extend of class, and need for use.
override init() {...
Note that any class in Swift must have initializer.
And in your case nevertheless generic, class must have init()

How can I use willSet in a UIViewController property

I'm trying to play a little bit with swift and iOS 8.
The question is, I'm trying to create this setter in a view controller:
- (void)setViewController:(UIViewController *)viewController
{
_viewController = viewController;
method do something
}
This code below is in Objective-C so I'd like to create it in swift.
I think, I should use willSet, but I know how do it if it's defined when you have your own variable, how can I define that willSet method if that property is a ViewController property.
Thanks
Something like this:
var viewController: UIViewController? {
willSet {
//method to do something
}
}
You can actually access what viewController will be set to with the variable "newValue" if you need that in the method call.
Note: I made the property optional here just to get rid of compiler warnings about needing initialization
An alternate way different from #Eric's answer is by using a computed property, which mimics objective-c properties more closely, consisting on defining a pseudo-private data member and a computed property implementing a getter and a setter:
class MyClass {
var _viewController: UIViewController?
var viewController : UIViewController? {
get {
return self._viewController
}
set {
self._viewController = newValue
// Do something else
}
}
}
I defined the property pseudo-private because swift doesn't have access modifiers (yet), so everything is public - but when I see an underscore prefixing a variable or property name, that to me means private (It's a convention I use it a lot in javascript for instance).

Resources