Initializing swift class in objective c project causes infinite loop - ios

I'm trying to add a color picker in my add and I use this https://github.com/gizmosachin/ColorSlider library which is only written in swift and I use objective-c. I have followed this guide How to call Objective-C code from Swift on how to add swift libraries in objective-c projects. I'm 99% sure that I have properly configured xcode because I can import the swift library and run my app without an error, it's only when I try to instantiate the swift class that the app crashes and I see that the init method for the swift class is called infinitely. The source code for the library is one file and listed for reference just in case (https://github.com/gizmosachin/ColorSlider/blob/master/Source/ColorSlider.swift)
Here is one of the init methods (the other inits are overrides)
// MARK: Initializers
convenience init() {
println("hi there swift")
self.init()
backgroundColor = UIColor.clearColor()
}
in my log I see "hi there swift" print out many times. This is how I initiate the swift class
ColorSlider *colorSlider = [[ColorSlider alloc] init];
I know that the function containing the line of code above is only being called once because I used NSLog(#"output") to see how many times this shows up and the output looks like this
output
hi there swift
hi there swift
hi there swift
hi there swift
hi there swift
etc...to infinity or until app crashes
Am I instantiating the swift class correctly? I'm not sure why the swift class's init method is called infinitely
----UPDATE-----
It looks as if the init methods below also use super.init?

As in Objective-C a class can have convenience (secondary) initializers and designated (primary) initializers.
The path of code execution should be:
A convenience initializer should call a designated initializer with self.designatedInit().
A designated initializer should call a super's designated initializer with super.designatedInit().
In your code, init() is a convenience initializer (convenience init()). calling self.init() would lead to an infinite loop, because this is the actually running function itself as Dan said.
If you change it to super.init() a convenience initializer is calling a super's initializer, what is illegal because of the above rule #1.
What to do?
Check, whether init() is really a convenience initializer.
If so, call self.designatedInit() instead of self.init().
If not so, change the classification of init() and call super.init() (or whatever is the designated initializer of the super class.

Remove self.init. The method is calling itself.

Related

Is it possible to run initialization code just after the default init code runs?

During iOS development I'm using a lot of classes which come from proprietary code which I have no access to. I want to run custom code that should run when this class is instantiated into an object, but I don't know how to, as I can't really override their init without knowing what parameters said init takes.
For example I am working on a SceneDelegate which subclasses UIResponder and UIWindowSceneDelegate and I want to have a property which should get initialized when the class is first instantiated, but I don't know how to.
At the moment I am just defaulting to defining the property as optional, initialize it in the method that will first use it, and write a whole bunch of code all around this class to handle the fact that it is unnecessarily optional.
Is there a better way?
Thanks!
You may try to make a class extension with your own init()?
extension SceneDelegate {
init(someParam: ...) {
....
}
}

How does UIViewController manage to have a default no-args initializer in Swift?

UIViewController class defines a single designated initializer, init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) and no convenience initializers, however, it is possible to write the following line of code and have it compile (Xcode 6.1.1)
let vc = UIViewController()
How is this possible?
According to the Swift book, here are the rules of initializer inheritance
Rule 1 If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.
Rule 2 If your subclass provides an implementation of all of its
superclass designated initializers—either by inheriting them as per
rule 1, or by providing a custom implementation as part of its
definition—then it automatically inherits all of the superclass
convenience initializers.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
https://itun.es/us/jEUH0.l
Therefore UIViewController couldn't have inherited the init() method from its ancestor superclass NSObject, then where does the initializer come from?
On a different note, because every swift class must ultimately call its designated initializer, doesn't that mean initWithCoder: will also end up calling init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)? However that doesn't seem to be the case in practice.
The rules in the Swift book apply to classes defined in Swift. UIViewController is a class imported from ObjC, so its behavior is subject to ObjC rules (or lack thereof).
In ObjC, the designated initializer chaining pattern is something you should do (unlike in Swift where the compiler enforces that it's the only thing you can do). If you call an inherited initializer instead of the designated initializer, you'll get back an instance... but there are no guarantees that said instance will be in a sensible state. (In fact, you're pretty much guaranteed it won't be.) Authors of ObjC classes sometimes practice defensive coding by implementing the initializers you're not supposed to call and having them throw exceptions, but that doesn't seem to be the case here.
This particular case is arguably a bug of some sort — it wouldn't be a bad idea to file one with Apple. Either Swift should enforce initializer rules when creating objects from imported ObjC API (in which case UIViewController would need to declare its designated initializers in ObjC with NS_DESIGNATED_INITIALIZER), or UIViewController should be implemented to guard against improper initialization. (Though it's possible there could be compatibility side effects to "fixing" either of those.)
The comment block above that init function contains the following text:
/*
The designated initializer. If you subclass UIViewController, you must call the super implementation of this
method, even if you aren't using a NIB. (As a convenience, the default init method will do this for you,
and specify nil for both of this methods arguments.) ...
*/
It is my understanding that because this is an Objective-C class, it is allowed to inherit methods implicitly. If you create a subclass of this class in swift, you will not have access to super.init(). If you create your subclass in Objective-C code without implementing your own -[ init], you will be able to construct your subclass with ().
Because your project drops into Objective-C land with the class that you're calling, it is able to find the selector. A swift subclass of an Objective-C class would follow the swift inheritance rules only.

Calling Swift from Objective C, closure to block?

I started converting an Objective c project to Swift.
First thing I am converting is the network layer.
I'm trying to write my network layer in Swift in a way that it can be called from objective c.
The problem is in my swift code I'm passing closures to a method. It works in Swift, but doesn't seem like when generating header files it generates method definition for any method that is using closures?
Is there a workaround for this, or any way where I could have a swift method that takes a closure/block?
Swift class
#objc public class UserNetworkManager: BaseNetworkManager {
// I can call this from objective c
public func fetchSomething() {
}
// I can't call this from objective c
public func fetchFriends(userId: String, completion: ([User]?, NSError?)->()) -> Request {
}
}
Generated Header
SWIFT_CLASS("_TtC12Company19UserNetworkManager")
#interface UserNetworkManager : BaseNetworkManager
- (void)fetchSomething;
#end
There shouldn't be any issue converting your closure to a block as long as every type involved can be translated to Objective-C. In this case, it's likely that you haven't tagged your User and Request classes as #objc, which would prevent this method from being generated.
To instantiate an Objective-C class in Swift, you call one of its initializers using Swift initializer syntax.
Objective-C initializers begin with init, or initWith: if the initializer takes one or more arguments. When an Objective-C initializer is imported by Swift, the init prefix becomes an init keyword to indicate that the method is a Swift initializer. If the initializer takes an argument, the With is removed and the rest of the selector is divided up into named parameters accordingly.

In Swift, will future new APIs be overridden by user-defined methods in existing apps on the App Store?

In Objective-C, for example, if Apple adds new method called method1 to UIView, existing apps which are already released to the App Store and use the following code could crash or behave unexpectedly:
// Objective-C
#interface MyView : UIView
- (void)method1;
#end
// Swift
class MyView : UIView {
func method1() {
// do something
}
}
But in Swift, to override a method, you need a override keyword to prevent overriding accidentally.
If you override a method without override keyword, the compiler generates compile-time error.
What will happen if Apple adds new API methods in the next iOS versions, and if my apps or your apps use methods whose name are the same as the new APIs' names.
In Swift, will new API methods be overridden by methods in existing apps like Objective-C?
Or new APIs don't effect existing same-name user-defined methods thanks to Swift's explicit override feature (override keyword)?
If your app is already built and uploaded no you won't have an issue.
But if you try to re-build the app for a new update and you have the bad luck they named a new Api Method the same name of your Object Method, most likely the error you might get is only if the place you use the method you don't use a correct identifier like Not calling self.method1() and just calling method1() and your object inherits from UIViewController, which coincidently has that new Method1.
other than that I wouldn't worry about that type of issue happening, well at least I haven't had an issue like this the 3-4 years I have been programming for iOS.
//Lets assume UIViewController has a new Method in the Api now which
//they updated called Method1
class MyViewController :UIViewController {
init {
//When you try to re-build your app this line of code right here would complain
//because of ambiguity, two methods called the same one from your
//Parent Class and your own Class.
//method1()
//"super.method1()" or code below to solve the ambiguity issue.
self.method1()
}
//Added this just because I happen to use UIViewController.
override viewDidLoad() {
super.viewDidLoad()
}
//Your own method without Override, since you want to use your own method.
func method1() {
//Does something important
}
}
Update Based on your comment:
1) Is there a documentation about it or did you tested it yourself in Swift?
I have tested it myself because I have Apps in the App Store. So what happens is that whatever is already uploaded the code will be working because the App you uploaded prepackages the Frameworks with their current working Apis and your Classes.
And No, I have not seen a Documentation about it, I know about it because I have personally see it.
2) And I think you can't define func method1() without a override keyword if UIViewController already has func method1()
Thats correct! Assuming the APi has a method already you have the write Override keyword to be able to use that function with the same name. But remember according to your scenario you mentioned that the API created that method with that name AFTER you had already your project uploaded in the AppStore. So the problem you would only see it when doing some new coding and trying to Rebuild the app.

Calling super.init() in initializer of NSObject subclass in Swift

I'm building an iOS app in Swift and drawing on the Lister sample project Apple provides.
Lister uses two model objects: List and ListItem. I found that both of them do not call super.init() in their initializers even though they subclass NSObject.
However, in the Objective-C version of Lister, both model objects (AAPLList and AAPLListItem) do call [super init].
The Swift Programming Language clearly states that “designated initializers must call a designated initializer from their immediate superclass.” (Rule 1 of Initializer Chaining in Initialization)
What's going on here? Why is this an exception and if you shouldn't always call super.init() in a subclass, what rules do apply?
Even though I can't find a place in the documentation where this is described, what happens is that the default superclass initialiser is called at the end of the subclass initialiser if that is the only initialiser of the superclass, and it wasn't called explicitly.
NSObject only has the default initialiser (init()); you can see that the superclass initialiser is called at the end of the subclass initialiser by attempting to reference self (eg. println(self)) in a constructor that does not call super.init(): You are not allowed to do it because the class is not fully initialised at that point.
If you want to use self somewhere in the constructor, the object needs to be fully constructed at that point, so you need to call super.init() manually before then.

Resources