How to know super class designated initializers? - ios

I am in need of subclassing, compiler warning says "Must call a designated initializer of the superclass", but i can not find them, where should i look for designated initializers?, i would like to know where to look for them independent of the class for future subclassing.
code:
import Foundation
import UIKit
class JBSOSLongGestureRecognizer: UILongPressGestureRecognizer {
init() {
super.init()
self.allowableMovement = 40
self.minimumPressDuration = 5
self.cancelsTouchesInView = false
self.numberOfTouchesRequired = 1
}
}

UILongPressGestureRecognizer inherits from UIGestureRecognizer. The designated initializer for UIGestureRecognizer is public init(target: AnyObject?, action: Selector).
Override init(target: AnyObject?, action: Selector) instead of init().
I found the designated initializer by jumping to the definition of UILongPressGestureRecognizer. I didn't see an initializer there, but I did see that it was a subclass of UIGestureRecognizer. Stepping into the UIGestureRecognizer declaration revealed the public initializer.
This information is also available in the Apple API docs. The documentation will say Designated Initializer next to the designated initializer.
UIGestureRecognizer Documentation

UILongPressGestureRecognizer is a concrete subclass of UIGestureRecognizer. So while you can subclass it using the method #JAL provided it is not intended to be subclassed.
In your code it doesn't seem like you are adding any functionality to your JBSOSLongGestureRecognizer, why not just use UILongPressGestureRecognizer and configure it with the values you want?

Related

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 private access control causing issues

I'm working on an iOS application and I'm using swift in that. For more readability and organising functions I've used extensions in my swift file.
// MARK: Class Declaration
class PaymentView
{
// Some stuffs
}
// MARK: Lifecycle methods
extension PaymentView
{
// Overriden for adding gesture recogniser
override func awakeFromNib()
{
super.awakeFromNib()
// Causes a crash when tapped on view
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("paymentViewSelected:"))
self.addGestureRecognizer(tapGesture)
// Works correctly !!!
paymentViewSelected(tapGesture);
}
}
// MARK: Private Methods
extension PaymentView
{
private func paymentViewSelected(sender : UITapGestureRecognizer)
{
print("Method called")
}
}
My issue is when I tap on my view the application crashes with unrecognised selector error. If I remove that private access control specifier from the method it works perfectly.
My question is, I can call the paymentViewSelected: directly from the awakeFromNib regardless the private. But why it is crashing when used as a selector ?
According to Swift AccessControl Reference
Private access restricts the use of an entity to its own defining
source file. Use private access to hide the implementation details of
a specific piece of functionality.
But my class, extension, gesture all are in same file. I think I'm missing some basic key concept here. Please help me to understand the issue.
About Your point : I can call the paymentViewSelected: directly from the awakeFromNib regardless the private. But why it is crashing when used as a selector ?
Its because methods that are marked with private can accessible inside the class, but the object of class cannot call the method marked with private.
In your tapGesture, it is called using object of class automatically, once you tap on the view.
In this case your method is not available because it is marked with private, this is the reason of crash error unrecognised selector.
If you are calling a method from a selector and it is private they cannot be called because the method is called from outside. When you call paymentViewSelected() in the awakeFromNib it is called inside the class. However when it is called via Selector, it is called from outside. like object.paymentViewSelected(). You cannot call private method from outside.

Swift: Unable to override operationDidFinish from GroupOperation class

In a custom subclass of GroupOperation, I'm trying to override operationDidFinish(). When I attempt to implement the function in my subclass, I get this error message:
Method does not override any method from its superclass
If I remove the override keyword, I get
Method 'operationDidFinish(:withErrors:)' with Objective-C selector
'operationDidFinish:withErrors:' conflicts with method
'operationDidFinish(:withErrors:)' from superclass 'GroupOperation'
with the same Objective-C selector
Weirdness. I'm pretty sure my method signature is spot on, and I'm not trying to overload an obj-c method, so all should be well. What gives?
For reference, my class looks like this:
class ServerAuthenticationOperation: GroupOperation {
// properties... initializer stuff...
override func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) {
print("I finished!")
}
}
I assume you're using Swift 2.
Objective-C does not support method overloading, so you have to select a different name for your method. Or, you can try these options:
Rename the method using the #objc(newMethodName:)
Use #nonobjc
Edit:
It seems working for the repo you provided, you can check it here. https://www.dropbox.com/s/hb07u3hyjhjuews/OverrideTest.zip?dl=0

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

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').

Resources