Swift: How to override/forward init call to my singleton initilializer? - ios

I found out that a singleton in swift is easy (credits go to krakendev.io):
class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
But I was not able to do this with a subclass and required designated initializers in the superclass. When I try to define all designated initializers private, compiler say that required init method from super must be accessible.
How can I make a subclass (for ex. from UIViewController) to be a singleton with exactly one instance and forward all init-calls to my singleton instance?? I cannot modify self in my init, too('self is immutable').

Related

How to Mock an object with protected init

I am trying to Mock an amazon services object to perform UnitTesting on a related code. I have done it as follows but each time the init is hit it crashes with error failed:
caught "NSInternalInconsistencyException", "- init is not a valid
initializer
Normally the same object will be created using there factory method, so it seems the initializer is made private or something. How can such object be mocked ?
class MyAWSiOTDataManager : AWSIoTDataManager {
override func publishString(_ string: String, onTopic topic: String, qoS qos: AWSIoTMQTTQoS) -> Bool {
print("publish string called")
return true
}
override init() {
}
}
let manager = MyAWSiOTDataManager()
You must call the designated initializer of AWSIoTDataManager inside your init call and remove the override decorator. That is why you got the error.
You can not mock something by subclassing it. With Swift, mocks are usually provided through a shared protocol. Define a protocol for the interface that you use from the third party library. Create an empty extension on the library with your protocol (there should be no code needed). Then implement a mock object against the protocol for use in your tests.

Designation & Convenience Initializer

I have a query regarding initializer in swift. As per Apples' Swift3.0
A designated initializer must call a designated initializer from its immediate superclass.
A convenience initializer must call another initializer from the same class.
A convenience initializer must ultimately call a designated initializer.
Now I have two class A & B (subclass of A). A & B both have two designated initializer with below structure
Can anyone suggest how class B's designated initializer will call to class A's initializers?
Since you have two designated initializers in A, these are non-ambiguous due to different signatures, and can be differentiated from each other. This means you can readily call the appropriate designated initializer of A from the subclass B. E.g., for A as
class A {
var a: Int
// designated "#1"
init() {
a = 0
}
// designated "#2"
init(_ a: Int) {
self.a = a
}
// convenience initializer for A
convenience init(_ foo: String) {
print(foo)
self.init(42)
}
}
We have, e.g.
class B: A {
let b: Int
// designated "sub#1"
override init() {
b = 42
super.init() // use A's designated #1
}
// designated "sub#2"
override init(_ a: Int) {
b = 24
super.init(a) // use A's designated #2
}
// non-overriding designated "#3"
init(b: Int) {
self.b = b
super.init() // use A's designated #1
}
// convenience initializer for B
convenience init(_ foo: String) {
print(foo)
self.init() // use B's designated #1
}
// you may only call designated initializers of the superclass
}
Note however that the initializers of a subclass may only call designated initializers of its superclass. This is covered by rules 1 and 2 in the Language Guide - Initializers - Initializer Delegation for Class Types [emphasis mine]:
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.
So you may not access the convenience initializers of A from the convenience initializers of B, and the designated initializers of B must naturally, by rule 1 above, call only the designated initializers of A.

UNNotificationCategory subclass init issue

I want to subclass UNNotificationCategory(UserNotifications), because I want to use enums instead of hard coded strings as category identifiers.
There is one convenience init inside UNNotificationCategory definition
public convenience init(identifier: String, actions: [UNNotificationAction], intentIdentifiers: [String], options: UNNotificationCategoryOptions = [])
I am not able to write an initializer for my subclass.
I understand I cant have designated initializer inside the subclass because I want to call the convenience init of superclass. But my convenience init is also throwing complier error.
Here's the code:
enum PushNotificationCategoryIdentifier:String {
}
convenience init(categoryIdentifier:PushNotificationCategoryIdentifier, actions:[UNNotificationAction], intentIdentifiers:[String], options: UNNotificationCategoryOptions) {
self.init(identifier: categoryIdentifier.rawValue, actions: actions, intentIdentifiers: intentIdentifiers, options: options)
}
This is resulting in error: self.init isn't called on all paths before returning from initializer
I guess this is because this class is implemented in Objective-C and may be they have not called the designated initailizer from convenience initailizer(as Objective-C classes dont have to call designated initializer from convenience initailizer).
But does that mean I can't subclass UNNotificationCategory if I want to write an initializer in it?
No you can do this. You will have to define init() method for this. Right now you have only defined convenience init(). But you will have to define init()in your subclass.
When you write a convenience init() it is only there to help initialization in an easy way but still you will have to call designated init with syntax init() from the convenience init().
You can read it on Apple Official Documentation

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.

Swift Declare Class Func in Protocol

I had following confusion. As far as I know the main difference between static and class keywords when declaring method is that the second one could be overridden in subclasses.
The problem
However when I declare a protocol in Swift 1.2 like this:
protocol MyProtocol
{
class func dummyClassMethod()
}
compiler gives an error:
Class methods are only allowed within classes; use 'static' to declare
a static method
The error is pretty descriptive as obviously MyProtocol is not a class, however I want to make a class func part of the protocol.
What I've tried
I've found that if I declare interface in protocol as static, compiler is happy and I could use this static method in all classes that adopt this protocol:
protocol MyProtocol
{
static func dummyClassMethod()
}
The question
So my question basically is is this right? This declaration states that my class method cannot be overridden in children, however in my implementation I could write and use the following:
class ClassA: MyProtocol
{
class func dummyClassMethod() {
}
}
class ClassB: ClassA
{
override class func dummyClassMethod() {
}
}
and now my dummyClassMethod is not static anymore...
Compiler is Ok and everything works - but why?
Is it specific to the fact that interface itself is static, however
it's implementation is not?
Is there a better alternative for class func in protocols?
Objective-C solution
In ObjC this is pretty easy and compile & run flawlessly:
#protocol MyProtocol
+(void)dummyClassMethod;
#end
You can review Apple's Documentation (subsection Method Requirements).
There says:
As with type property requirements, you always prefix type method requirements with the static keyword when they are defined in a protocol. This is true even though type method requirements are prefixed with the class or static keyword when implemented by a class
In practice, You can do it as follow:
First, declare your protocol:
protocol SomeProtocol {
static func someMethod()
}
Then, in your class you've 2 options:
First:
class SomeClass : SomeProtocol {
class func someMethod()
}
Second:
class SomeClass : SomeProtocol {
static func someMethod()
}
I hope, this may clarify your doubt..
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
A protocol defines a blueprint of methods, properties, and other
requirements that suit a particular task or piece of functionality.
The protocol doesn’t actually provide an implementation for any of
these requirements—it only describes what an implementation will look
like. The protocol can then be adopted by a class, structure, or
enumeration to provide an actual implementation of those requirements.
After this protocol definition it becomes reasonable that
As with type property requirements, you always prefix type method
requirements with the static keyword when they are defined in a
protocol. This is true even though type method requirements are
prefixed with the class or static keyword when implemented by a class...
To make protocol method static and final implement that method with static keyword
class ClassA: MyProtocol{
static func dummyClassMethod() {
}
}
and now you cant override dummyClassMethod function anymore. If you want to prevent overriding only you must declare protocol method as final. About class functions, they were not fully supported in Swift 1.0 and now in Swift 1.2 I think that they are moving towards static functions

Resources