Why Does a Public Class/Struct in Swift Require an Explicit Public Initializer

Why does a public class/struct in Swift require an explicit public initializer?

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.

How can I make the memberwise initialiser public, by default, for structs in Swift?

Quoting the manual:

"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.

As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."

Excerpt from "The Swift Programming Language", section "Access Control".

Why do I need to write initializer for struct in Swift in order to use it in unit tests?

This is fixed in Swift 2.0 with the @testable attribute.

If you import your module into the tests with @testable the synthesized initializers will become visible to your tests.

You can find a brief intro to @testable here.

Why Swift class need init but not Swift struct

Structs still have an initializers. The only differences is that in some cases the compiler will synthesize a "default member-wise initalizer" for you.

In this case, it created one with the signititure private init(value: Int) (private because your struct has a private field)

What is the meaning of public init() { } in Swift 3?

When you mark public, the thing gets available outside of the framework in which your code has been implemented whereas init() {} is a swift initializer that is responsible for ensuring the object is fully initialized. Basically initializers are called to create a new instance of a particular type. In its simplest form, an initializer is like an instance method with no parameters.

init() {
// perform some initialization here
}


Related Topics



Leave a reply



Submit