3

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() {}
}
Boon
  • 40,656
  • 60
  • 209
  • 315
  • I guess that omitting the `public` is just syntactic sugar. Have you tried to make it `private`? xD – qwerty_so Nov 20 '15 at 16:41
  • Without public init is internal in scope. So I am wondering what the difference is. Setting to private doesn't cause compile time error either. – Boon Nov 20 '15 at 16:44
  • @ThomasKilian to be more clear, the default access modifier in Swift is `internal` as opposed to `public` or `private`. – nhgrif Nov 21 '15 at 13:21
  • @nhgrif This is most confusing. Declaring an init as private and being able to use it. To me that feels "naturally contradicting". – qwerty_so Nov 21 '15 at 14:21
  • Where are we declaring anything as private? Internal and private are different access levels. What are you confused about @ThomasKilian? – nhgrif Nov 21 '15 at 17:18
  • @nhgrif An init is public per se (how else would you init an object). So marking it as private seem non-logic. – qwerty_so Nov 21 '15 at 23:23
  • @ThomasKilian An `init` isn't "public per se" (whatever that means). And marking initializers as having a lower access modifier than the class is not at all uncommon (the most common counter point being singletons). There are all sorts of reasons why we might want at least some of our class's initializers to have a lower access modifier than the class. – nhgrif Nov 21 '15 at 23:47

2 Answers2

3

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.

Community
  • 1
  • 1
nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • Per compiler, required initializer has to be public if the class is public, but the rule does not apply to initializer not marked as required, hence my question. Any idea why? – Boon Nov 21 '15 at 14:31
  • That's not ***this*** question. That is your other question. ***This*** question is about designated initializers and whether or not they have to be `public`. My answer demonstrates they do not. Your *other* question asks about access modifiers of `required` initializers and if you consider the current answer there insufficient, I can write an answer there later today. – nhgrif Nov 21 '15 at 17:21
  • You are right I got confused. Answer selected. Thanks for the insightful response. – Boon Nov 21 '15 at 17:41
  • Wonderful explanation of designated and convenience initializers. Thanks so much! One question: convenience initializers don't need to call designated initializers immediately, right? There are examples where they do other things before calling the designated initializers, so just want to clarify what you meant by `delegates to a designated initializer first`. – Crashalot Feb 11 '16 at 01:51
  • 1
    @Crashalot You must call to a designated initializer before you use `self`. So if you haven't called to a designated initializer, you cannot assign to properties in the class nor can you call instance method on the class (or just use `self` as an argument to anything, etc). Until you've called a designated initializer, your object is not fully instantiated and cannot be used. – nhgrif Feb 11 '16 at 13:21
  • Interesting, thanks! OK so you can do other things, like define non-self variables, but can't do anything with `self` until a designated initializer is invoked? – Crashalot Feb 11 '16 at 17:51
1

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.

subjective_c
  • 282
  • 2
  • 10
  • Thanks - I did find public to be necessary if I made init required, but that's not the case if the init is not marked required. – Boon Nov 20 '15 at 16:45
  • This answer is misleading and potentially wrong. I will check whether or not it is right later, but best case scenario, this answer is very far from complete. – nhgrif Nov 21 '15 at 13:14
  • This answer needs more attention. I was working within a workspace with a few dynamic frameworks set up, and I was unable to access the initializer of a class in Framework A, from within Framework B. Making the initializer `public` fixed it. Good link on access control as well. – gokeji May 06 '16 at 19:33