Swift init(), defining custom init is it overriding? - ios

I have some confusion over this:
class Person {
var name: String
var age: Int
init(){
name = “Tim”
age = 30
}
}
A basic custom init. What exactly is the code below doing?:
init(){
//Code here
}
It's not an override because we don't use override func init(). Someone said it's actually calling a default init method that comes with the class. If that's the case then what do the curly brackets do?

Because you haven't subclassed from NSObject, there is no init() method to override. Hence why you don't need it in this case.
In this case, nothing else is called when you call your init() method, there are no other default initializers. The curly brackets are there simply because you're not accepting any arguments to your init method. You could for example accept some arguments:
init(withName name: String, andAge age: Int) {
self.name = name
self.age = age
}
And call it like so:
Person(withName: "Chris", andAge: 23)

Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
You can customize the initialization process with input parameters and optional property types, or by assigning constant properties during initialization.
You can provide initialization parameters as part of an initializer’s definition, to define the types and names of values that customize the initialization process. Initialization parameters have the same capabilities and syntax as function and method parameters.

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()
}
}

How to automatically create an initializer for a Swift class?

UPDATE:
Use structs and not classes. Struct is better in many ways has got an initializer of its own.
This is my model class. Is it possible to create the init method automatically? Everytime I have to initialize all the variables one by one and it costs a lot of time.
class Profile {
var id: String
var name: String
var image: String
init(id: String, name: String, image: String) {
self.id = id
self.name = name
self.image = image
}
}
I want self.id = id and other variables to initialize automatically.
Update As of Xcode 11.4
You can refactor (right-click mouse menu) to generate the memberwise initializer for class and struct.
Note that struct automatic initializers are internal. You may want to generate memberwise initializer when defining a module to make it public.
Right-click > Refactor > 'Generate Memberwise Initializer'
For older Xcode
There are handy plugins:
https://github.com/rjoudrey/swift-init-generator https://github.com/Bouke/SwiftInitializerGenerator
Given the following class (or for structs if temporarily change the keyword struct to class and after refactor set back to struct):
class MyClass {
let myIntProperty: Int
let myStringProperty: String
let myOptionalStringProperty: String?
let myForcedUnwrappedOptionalStringProperty: String!
}
Go to Xcode and:
Double click the class name
Right click
Refactor
Generate Member-wise Initializer
Above steps look like this:
Just a tiny second later, Xcode generates this initializer:
internal init(myIntProperty: Int, myStringProperty: String, myOptionalStringProperty: String?, myForcedUnwrappedOptionalStringProperty: String?) {
self.myIntProperty = myIntProperty
self.myStringProperty = myStringProperty
self.myOptionalStringProperty = myOptionalStringProperty
self.myForcedUnwrappedOptionalStringProperty = myForcedUnwrappedOptionalStringProperty
}
No, there is no such feature for classes. But, if you design this as a struct, you get an memberwise initializer for free — assuming you don't define others initializers yourself.
For instance:
struct Point {
var x: Float
var y: Float
}
...
var p = Point(x: 1, y: 2)
From The Swift Programming Language book:
Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers. Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that do not have default values.
The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.
You can use struct if you want automatically created init without actual need to define it.
If you want to define it as fast as possible for classes, you can use automatic initializer completion luckily introduced in Xcode 14 🥳 (source - 60399329)

Why is the superclass designated initializer getting called by default? [duplicate]

This question already has an answer here:
Why doesn't Swift force my designated initializer to call super?
(1 answer)
Closed 6 years ago.
I am new to Swift and getting some problem regarding initializers. I have created a Swift file with the following code :
import Foundation
class SuperClass
{
var a : Int
init()
{
a = 10
print("In Superclass")
}
}
class SubClass : SuperClass
{
override init()
{
print("In Subclass")
}
}
In the above code, init() of SubClass does not contain call to init() of SuperClass i.e. there is no super.init() in SubClass init().
So my question is:
1. Why is it not giving any error if I don't call designated init() of SuperClass
2. If I am creating an object of SubClass i.e. let s = SubClass(), the output is :
In Subclass
In Superclass
Why the init() of SuperClass is getting called? Does a subclass init() calls the superclass init() by default?
As far as I understood your question, you're not only wondering why, when and how the initializer gets called automatically, but also complaining about the missing documentation of this behavior.
First of all I agree with you on the lack of documentation - just like you I'm not able to find anything about this behavior and therefore it should be added to the documentation by Apple.
Why super.init() is called:
As per documentation a designated initializer of the superclass has to be called by a designated initializer of its subclass in order to fully initialize all properties.
Rule 1
A designated initializer must call a designated initializer from its
immediate superclass.
Your code example above proves it's obviously done implicitly: print("In Superclass") prints to the console, so super.init() is somehow invoked while creating an instance.
When and how super.init() is called:
There are some conditions to be met in order to allow the compiler to call the designated initializer of the superclass implicitly:
The superclass must have only one designated initializer which
is then called. Otherwise the compiler had to choose one to delegate
to. This single designated initializer could be also the default
initializer or an inherited initializer.
class SuperClass {
var a: Int
init() {
a = 10
}
// introduction of a second designated initializer in superclass:
init(withValue value: Int) {
a = value
}
}
class SubClass: SuperClass {
// won't compile:
// "error: super.init isn't called on all paths before returning from initializer"
override init() {}
}
The single designated initializer of the superclass mustn't have any
parameters. After all the compiler wouldn't know any appropriate
parameter to be passed.
class SuperClass {
var a: Int
// declaration of an initializer with parameter:
init(withValue value: Int) {
a = value
}
}
class SubClass: SuperClass {
// won't compile:
// "error: super.init isn't called on all paths before returning from initializer"
override init() {}
}
The designated initializer of the subclass mustn't further read or
modify (inherited) instance properties of the superclass or call
instance methods of the superclass. That's because of Swift's
two-phase initialization process with its corresponding safety
checks and the fact that the implicit delegation up to the designated
initializer of the superclass happens at the end of the
init-Statement in the subclass.
Safety check 2
A designated initializer must delegate up to a
superclass initializer before assigning a value to an inherited
property. If it doesn’t, the new value the designated initializer
assigns will be overwritten by the superclass as part of its own
initialization.“
Safety check 4
An initializer cannot call any instance methods, read
the values of any instance properties, or refer to self as a value
until after the first phase of initialization is complete.
class SuperClass {
var a: Int
init() {
a = 10
}
}
class SubClass: SuperClass {
// won't compile:
// "error: use of 'self' in property access 'a' before super.init initializes self"
override init() {
a = 10 // modifying inherited self.a before phase 1 of initialization completes isn't valid!
// implicit delegation to super.init()
}
}
Safety check 1
A designated initializer must ensure that all of the
properties introduced by its class are initialized before it
delegates up to a superclass initializer.
class SuperClass {
var a: Int
init() {
a = 10
}
}
class SubClass: SuperClass {
// introduction of instance property "b"
var b: Int
// compiles finely:
override init() {
b = 10 // initializing self.b is required before delegation!
// implicit delegation to super.init()
}
}
I hope that helps.
Why the init() of SuperClass is getting called? Does a subclass init() calls the superclass init() by default?
Basically, yes.
If all the rules say that you should say super.init() and you don't say it, it is called for you.
I don't like this behavior; it is poorly documented, and besides, secretly doing stuff for you seems against the spirit of Swift. But I filed a bug against it long ago and was told it was intended behavior.
Every class have at least one designated initializer which is responsible for initializing instance variables.
Here is an extract from the doc :
Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.
Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described in Automatic Initializer Inheritance below.
You can refer to the complete documentation for further details : https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
A rule of thumb
Create one designated initializer for your class.
Call the designated initializer of the superclass or let the system figure this out for you.
Create zero or more convenience initializers which will call your designated initializer.
You are not accessing the super class variable in the subclass hence super.init() is called followed by the subclass's init. But if you were to try using the super class variable in subclass without calling its initialiser then it will result in a compile time error.
I didn't try this myself, but the Swift Language Guide says:
Initializer Delegation for Class Types
To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:
Rule 1
A designated initializer must call a designated initializer from its immediate superclass.
Rule 2
A convenience initializer must call another initializer from the same class.
Rule 3
A convenience initializer must ultimately call a designated initializer.
A simple way to remember this is:
Designated initializers must always delegate up.
Convenience initializers must always delegate across.
So, as it is a 'rule' to call super.init(), it might just be done internally, if not implemented explicitly.

Why latest Swift version always says variable is not initialized before use

Take an example
Class A {
var a : Int
override func viewDidLoad() {
super.viewDidLoad()
a=0
}
when it says variable is not initialized, even when i already declared in class first.
viewDidLoad is not the equivalent of init
I suggest you either use optionals:
var a:Int?
or you can initialize your variable directly in its declaration
var a:Int = 0
Last but not least, if you want to initialize any other way, do it in the init
override init() {
super.init()
a = 0
}
when you declare property in the class with Int it indicates it has no initializers. either give value at initialize time var a : Int = 0
or give value using init block which is use initialize the property of class. or you can also give declare as optional with ? var x : Int?
override init() {
super.init()
a = 0
}
Swift performs Two phase initialization :
Two-Phase Initialization
Class initialization in Swift is a two-phase
process. In the first phase, each stored property is assigned an
initial value by the class that introduced it. Once the initial state
for every stored property has been determined, the second phase
begins, and each class is given the opportunity to customize its
stored properties further before the new instance is considered ready
for use.”
Basically, this means that a property is not ready for use till it is provided an initial value.
In Objective-C, this was handled internally as the properties were set nil or 0 (depending on the data type) until initialized.
This behavior is provided by Optionals in Swift.
“You use optionals in situations where a value may be absent. An
optional says:
There is a value, and it equals x or
There isn’t a value at all”
As mentioned by other answers, you can declare an optional using a "?"
eg: var a : Int?
For details refer : The Swift Programming Language (Swift 2.2).

initializing class properties before use in Swift/iOS

I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash

Resources