How to Make the Memberwise Initialiser Public, by Default, for Structs in Swift

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

Swift how to make the default initializer of a `struct` private

If you are noticing that implementing:

struct Foo {
let bar: String
static let sharedInstance = Foo(bar: "blah")
}

is legal even without giving bar an initial value, why? because Swift Structs have a default initializer, called the memberwise initializer:

Structure types automatically receive a memberwise initializer if they
do not define any of their own custom initializers. Unlike a default
initializer, the structure receives a memberwise initializer even if
it has stored properties that do not have default values.

The memberwise initializer is a shorthand way to initialize the member
properties of new structure instances. Initial values for the
properties of the new instance can be passed to the memberwise
initializer by name.

Meaning that if you tried to create a new instance of Foo struct, the compiler should -automatically- provide:

Sample Image

However, you can get rid of it by implementing private init(), but you have to make sure that all stored properties have -dummy- initial values (or maybe let them optionals...) because the struct no longer has an initializer for guaranteeing that the stored properties has values.

It should be similar to:

struct Foo {
var bar: String = ""
static var shared = Foo()

private init() {}
}

Remark: I would suggest to stuck with class when implementing a singleton pattern, you might want to check this answer (Thanks to
MartinR for mentioning it).

swift - can I call a struct default memberwise init from my custom init method?

Add your own initializer as an extension to your struct. Extensions cannot remove existing functionalities, so it will preserve struct's default initializer.

struct OrderFill {
let price: Int
let qty: Int
let timeStamp: NSDate
}

extension OrderFill {

init(dict: [String: AnyObject]) throws {
self.init(
price: dict["price"] as! Int,
qty: dict["qty"] as! Int,
timeStamp: try parseDate(dict["ts"] as! String)
)
}
}

let o = OrderFill(someDict)


Related Topics



Leave a reply



Submit