Why does a public class/struct in Swift require an explicit public initializer?
swift, swift3
Solution
Marking a class public does not necessarily imply that the developer wants the class to be initialized publicly. For example, I often write base classes that exist solely for me to be able to subclass them. I give these superclasses `internal` initializers so that their subclasses can access them, but those in the outside world shouldn't be using them directly. For example, `Operation` in `Foundation` has no accessible initializers, yet the class is public. It is simply meant to be subclassed. This is considered an abstract class in Objective-C.
Since Swift doesn't contain explicit support for abstract classes, the act of making a class public but without public initializers basically serves as an abstract class (except each function must still have a default definition, either in the class itself or some protocol extension).
With this in mind, here are some Swift rules:
- If your class is marked `private`, all variables, inits, and functions will default to `private`.
- If your class is marked `internal` (which is default), `public`, or `open`, all variables, inits, and functions will default to `internal`.
- A subclass's superclass must be at least as accessible.
- classes and class members declared `public` in Objective-C are imported into Swift as `open`, due to there being no such distinction in Objective-C.
That second one is the one you are running into. The default init is picking up the default `internal` because the last thing Swift wants to do is expose your init as public API unless it is explicitly instructed to do so.
Note: In my tests (at least in the playground), it seems that with the introduction of `fileprivate`:
- If a class is declared `private` or `fileprivate`, it seems that class members default to `fileprivate` unless explicitly annotated `private`.
Problem
Consider the follow class (equally applicable to a struct, as well) in a module: ``` public class Foo { public func bar() { // method body } } ``` Note, it does not have an explicit initializer; this example doesn't need any special initialization. This class would be exposed to other modules because it is marked `public`. However, when code outside the module attempts to initialize it, the compiler complains: ``` let foo = Foo() // 'Foo' initializer is inaccessible due to 'internal' protection level ``` In order to satisfy the compiler, I have to define an explicit empty initializer marked `public`: ``` public class Foo { public init() { // This initializer intentionally left empty } public func bar() { // do something useful } } ``` Why, if the class is explicitly `public`, do I need to explicitly define a public initializer? Shouldn't it implicitly have a public initializer? There is a related question here, pertaining to unit testing, but I find it doesn't really get at the core of the design philosophy of what I find to be a surprising issue.