Convenience init vs self initialization - swift

Why would you use a convenience init instead of just setting a default value to the class property?

Convenience initializers can be used for all kinds of things that have nothing to do with default values. They can provide data conversions, or many other features. In this example, a convenience initializer allows you to pass a Float to something that requires an Int.
import Foundation
class X {
let value: Int
init(value: Int) {
self.value = value
}
convenience init(rounding float: Float) {
self.init(value: Int(round(float)))
}
}
Of course this could also be written as a designated (non-convenience) initializer, but delegating to a small number of designated initializers makes subclassing much simpler and can reduce code duplication (thus "convenience").

You do not always provide a default value to a class property, sometimes you create a class with properties and method and all the value of the property you might get it later when you create an instance of that class. For that purpose init is used.

Related

Understanding init() when sub-classing in Swift

How can I add a custom initializer when inheriting from a class that already has an initializer?
What I have is a Vehicle class which has an itializer who takes an argument name. What I want to do is inherit from this Vehicleclass, create another initializer for the new class but keep using the existing initializer.
Base Class (No problem here):
class Vehicle{
var make:String
init(make:String){
self.make = make
}
}
New Class (Doesn't work):
// Not sure how to structure this class
class Car:Vehicle {
var engine:Double
override init(){
super.init()
}
init(engine:Double){
self.engine = engine
}
}
This is what I would like to be able to do... re-use the existing initializer plus the new one.
let cobalt = Car(make:"Chevy" engine: 2.5)
Any designated initializer in a subclass must call a designated initializer from its immediate superclass:
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
...
From the Swift Language Guide - Initialization.
Hence, you could construct your designated initializer of Car to take two arguments, make and engine, and use the latter to inititalize the member property engine of the subclass, thereafter call the designated initializer of the superclass using the supplied make parameter (supplied to subclass initializer) as argument to the superclass initializer.
class Car: Vehicle {
var engine: Double
init(make: String, engine:Double){
self.engine = engine
super.init(make: make)
}
}

Providing a default value from a class function for a constant stored property in Swift initializers?

I would like to do something similar to the following with an NSObject subclass
class Thing: NSObject {
class func defaultText() -> String { ... }
let text: String
init(text: String?) {
self.text = text ?? self.dynamicType.defaultText() // Of course, this line won't compile
super.init()
}
}
so that Thing subclasses may override defaultText() without requiring them to override the initializer. This is easy to do with a mutable stored property, but it would be nice to have it constant. Is there a way to do this?
This is a separate issue than overriding static vars in subclasses swift 1.2 . There is no desire to override a constant or a static method. The only thing in question is, in the initializer where the constant is set, is there a way to compute a value based on the specific class that is being initialized?

In Swift, how do I create a convenience init for a class where the init's implementation creates the class value instead of calling an existing init

I feel like the answer is obvious, but I haven't been able to figure this out and it seems to be a recurring problem for me. Basically I want to do something like this:
extension NSData {
convenience init(JSONObject: AnyObject) {
do {
self = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: [])
}
catch {
self = nil
}
}
}
However it won't let me simply assign a value to self. I do this all the time with enums, but it won't let me do it with classes. Is there any way implement at convenience initializer using an instance of the class created in the initializer implementation?
Saying that factory initializers are "not supported yet" in Swift is fallacious. Their exclusion is a design decision, and their use intended to be covered by failable initializers; quoting the following Apple Swift blog post
Failable initializers eliminate the most common reason for factory
methods in Swift, which were previously the only way to report failure
when constructing this object.
...
Using the failable initializer allows greater use of Swift’s uniform
construction syntax, which simplifies the language by eliminating
the confusion and duplication between initializers and factory
methods.
So in your case, you're probably looking for a convenience failable initializer. E.g., something along the lines
extension NSData {
convenience init?(JSONObject: AnyObject) {
do {
let foo = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: [])
self.init(data: foo)
}
catch {
return nil
}
}
}
/* Example usage */
let foo : AnyObject = ["Foo":"bar"]
let bar = NSData.init(JSONObject: foo)
In the title of your question you include "... instead of calling an existing init". When making use of convenience initializer, a designated initializer (of same class) must be called at some point (even via other convenience initializers). From the Swift Language Guide - Initialization - Class Inheritance and Initialization:
...
Rule 2
A convenience initializer must call another initializer from the same
class.
Rule 3
A convenience initializer must ultimately call a designated
initializer.
The example code above, however, allows an early escape (failure) of the convenience initializer if NSJSONSerialization.dataWithJSONObject(...) fails, but if it succeeds, sooner or later a designated initializer needs to be called (in this case init(data:) designated initializer).
For details on failable initializers, see the Swift Language Guide - Initialization - Failable Initializers. For an additional remark regarding the initializer chain (convenience -> ... -> designated initializer), see rickster:s comment below.
If I understand you right you want a factory initializer. In Swift they are not supported yet. The best you could do is to use static factory method.

Swift initializer for public class

When creating a public class, is it necessary to make the designated initializer public?
What's the difference between making it public vs not?
e.g.
public class A {
init() {}
}
or
public class A {
public init() {}
}
No
You do not need to make it public. In fact, you can make it private. The only thing special about a designated initializer (and your classes are allowed more than one) is that they are responsible for ensuring the object is fully initialized.
A convenience initializer (an initializer is either designated or convenience) (marked by the convenience keyword) does not have this responsibility. A convenience initializer delegates to a designated initializer first, and then after that returns, it is given the opportunity to overwrite some of the initial values. Importantly, the convenience initializer must always "delegate across". It must always call another initializer within the class.
By contrast, a designated initializer (any initializer not marked with the convenience keyword) must take responsibility for making sure every property in the class gets a valid initial value, then it must "delegate up" (call a super class initializer, if there is a super class), then it can overwrite values if it wants.
But nothing prevents you from create a class with nothing but private designated initializers (nothing at all).
Even when we are inheriting from a class with a required initializer, our subclass can simply implement that initializer as a convenience initializer.
class ParentClass {
let foo: Int
required init(foo: Int) {
self.foo = foo
}
}
class ChildClass: ParentClass {
let bar: Int
private init(foo: Int, bar: Int) {
self.bar = bar
super.init(foo: foo)
}
required convenience init(foo: Int) {
self.init(foo: foo, bar: foo)
}
}
The above is perfectly valid Swift.
ChildClass has a private designated initializer. It has inherited from a parent class with a required initializer, but it has implemented that initializer as a convenience initializer. (Moreover, in contrast to your conjecture in a comment here and a question else where, the required initializer doesn't actually have to be public necessarily... context matters, and perhaps I will expand on that in an answer to your other question.)
Moreover, in direct contrast to subjective_c's answer, you don't have to make parts of your Swift code public in order to use that code from Objective-C, and the passage from Apple he has quoted in his answer actually indicates that. You only need to mark public the things you want to be available outside of the framework in which your code has been implemented. You can put Objective-C & Swift code within the same application and the Objective-C code can see anything marked as public or internal.
You don't need to make it public unless you are making it available as a framework for usage in Objective-C as well.
See: https://developer.apple.com/swift/blog/?id=5
When mixing Objective-C and Swift, because the generated header for a
framework is part of the framework’s public Objective-C interface,
only declarations marked public appear in the generated header for a
Swift framework.

Meaning of the convenience keyword in Swift

When I go back to look at some of Apple's Sprite Kit documentation I see a lot of occasions where a keyword called convenience comes up. For Example
convenience init(texture texture: SKTexture?, size size: CGSize)
What does it mean?
Convenience initialisers allow you to initialise a class without all the required parameters the designated initialiser needs.
For example, in a very basic example you may have a designated initialiser for a class that requires a String:
init someName(value: String) {
You could also create a convenience initialiser to go along side this that takes an int and converts it to a String and then calls the designated initialiser and passes it that String. This way if your class is initialised and passed an int instead or by mistake, it won't error and will handle it.
convenience init someName2(value: Int) {
let someString = String(value)
someName(value: someString)
}
Another use for them is that the designated initialiser may take multiple parameters. You could create a convenience initialiser to go along side this that takes only one of those parameters, creates the others and sets them to some default values and then calls the designated initialiser, passing them all in. This way you do not need to specify all required parameters of the designated initialiser since if you don't, your convenience initialiser will fill in missing ones with default values.
convenience init someName(value: String) {
someName(value: someString, value2: "DefaultValue")
}
init someName(value: String, value2: String) {
That is a convenience initializer.
From the docs:
Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
An initializer that is not marked convenience is a designated initializer:
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
The docs I linked above are very detailed, because there is a lot to initialization. You'll need to read them to really understand what's going on. But to give an example, let's say you create a car class:
class Car {
let numberOfWheels: Int
init(numberOfWheels: Int) {
self.numberOfWheels = numberOfWheels
}
convenience init() {
self.init(numberOfWheels: 4)
}
}
The car class contains two initializers - a designated initializer and a convenience initializer.
The designated initializer is simply marked init. Inside a designated initializer, you are required to set all class properties so that the class is ready to go once initialization finishes. We set numberOfWheels inside our initializer.
The convenience initializer is marked convenience init. A convenience initializer may do whatever, but it must call one of the designated initializers before it finishes. In our example, it calls self.init(numberOfWheels: Int) and supplies a default number of wheels (4).
Convenience initializers are there exactly for their namesake: convenience. They allow you to set up initializers to classes that deal with common initialization cases. In our Car class, it is typical for a car to have four wheels, so our convenience initializer makes that common case easier.
To create a car, we can use either the designated initializer or the convenience initializer:
let carA = Car(numberOfWheels: 3) // makes a car with 3 wheels
let carB = Car() // makes a car with 4 wheels
There are a lot more rules around initialization, especially in class hierarchies where super.init and initializer inheritance must be considered as well. I cannot describe them all any better or more succinctly than the official documentation, so again, I suggest you check there.
Default init:
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
convenience init:
Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
as per the Swift Documentation
Actually - Swift defines two kinds of initializers.
Designated initializers
Convenience initializers
Designated initializers are the primary initializers for a class. Every class should have at least one designated initializer.
init(parameters if any) {
}
Convenience initializers are secondary, supporting initializers for a class. Convenience initializers are written in the same style, but with the convenience modifier placed before the init keyword, separated by a space:
convenience init(parameters if any) {
}
Convenience init initializes the designated init method by calling self.init.
Example:
class HumanBeing {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: “not set”)
// Convenience init call the designated init method
}
}
let humanBeingObj1 = HumanBeing() // calls convenience init
let humanBeingObj2 = HumanBeing(name: “abhilash”) // calls designated init