Reputation: 26952
I have a Swift framework that defines a struct:
public struct CollectionTO {
var index: Order
var title: String
var description: String
}
However, I can't seem to use the implicit memberwise initialiser from another project that imports the library. The error is:
'CollectionTO' cannot be initialised because it has no accessible initialisers
i.e. the default synthesized memberwise initialiser is not public
.
var collection1 = CollectionTO(index: 1, title: "New Releases", description: "All the new releases")
I'm having to add my own init method like so:
public struct CollectionTO {
var index: Order
var title: String
var description: String
public init(index: Order, title: String, description: String) {
self.index = index;
self.title = title;
self.description = description;
}
}
... but is there a way to do this without explicitly defining a public init
?
Upvotes: 251
Views: 48382
Reputation: 406
To generate a public memberwise initializer automatically, you can use @MemberwiseInit, a Swift macro that I authored. Ensure that all properties are declared public. For example:
import MemberwiseInit
@MemberwiseInit(.public)
public struct CollectionTO {
public var index: Order
public var title: String
public var description: String
}
In 2015, Chris Lattner, one of the principal architects of Swift, acknowledged several “deficiencies” in the built-in memberwise initializer: lack of public access, disabled memberwise initializers when custom initializers are used, and complexities with classes and lazy properties. You can read his full post here. As Lattner then postulated, the “hygienic macro system” introduced some 9 years later in Swift 5.9 allows @MemberwiseInit
to address these issues.
Selective Access via @Init
Instead of making a property public (e.g., public var index: Order
), you can use @Init(.public)
to specify that a property be publicly accessible through the initializer, e.g. @Init(.public) var index: Order
. However, the practical difference between making a property public versus just exposing it through the initializer is usually minimal. In most cases, it’s better to mark the properties directly as public if they are intended to be used externally.
Upvotes: 1
Reputation: 11140
You have to define public
init by yourself, luckily starting from Xcode 14 🥳 there is an automatic initializer completion (source - 60399329)
Upvotes: 4
Reputation: 1223
Sometimes it's really annoying having an initializer when you don't need one. If you're constantly updating the variables to the object, it becomes bothersome very quickly to update the variables in 3 places (variable declaration, initializer parameter, and initializer implementation). A workaround I've used for this issue is to have a static variable on the struct to act as (or essentially wrap) the "initializer". For instance:
struct MyStruct {
static var empty = Self()
static func empty(name: String) -> Self {
.init(privateName: name)
}
private var identifier: String = ""
}
Then you can call it similar to how you would an initializer (with autocomplete and everything!):
func someFunction(_ value: MyStruct) { ... }
//someFunction(.init()) -> ERROR, invalid due to `private` variable
someFunction(.empty)
someFunction(.empty(name: "Dan IRL"))
let myObject = MyStruct.empty
let myObject2 = MyStruct.empty(name: "Monty Python")
Upvotes: 0
Reputation: 4066
While it is not possible to have the default memberwise initializer at least you can make one quickly with the following steps:
UPDATE: Xcode 11 and later
As mentioned by Brock Batsell on the comments, for Xcode 11 and later all you need to is this:
Xcode 10 and earlier answer
Upvotes: 182
Reputation: 26952
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".
Upvotes: 395
Reputation: 3699
We now have a ruby gem 💎 to parse a complete swift data model file, line-by-line, and add public access modifiers, public member-wise default initializers, and other things into a separate auto-generated output swift file.
This gem is called swift_republic
Please check out the following documentation for running this gem:
https://github.com/mehul90/swift_republic
Upvotes: 0