Swift Quick Tip - Struct Memberwise Initializers are Internal Only
A struct is a powerful building block in a Swift developer's tool belt. These simple pass by value Swift objects have 1 powerful and time saving feature over their class
counterparts and that is the fact that the Swift compiler automatically generates an init method for you.
struct MyStruct {
var myStringVar: String
var myIntVar: Int
}
struct MySecondStruct {
func myFunc() {
let myStruct = MyStruct(myStringVar: "myString", myIntVar: 0)
// do something with myStruct here
}
}
In the above code, the Swift compiler automatically created the initializer for MyStruct
. But what happens when MyStruct is inside of a Swift package and not inside your app's target? Let's find out. The following code is defined inside of a Swift package that is imported into your app via SPM.
public struct MyStruct {
public var myStringVar: String
public var myIntVar: Int
}
As you can see, this version of MyStruct
has marked itself and all of its properties as public
so that they are visible so code outside of the package. So, what happens when we try to initialize this struct from our app? The compile throws this error at you, 'MyStruct' initializer is inaccessible due to 'internal' protection level
.
Well, isn't this super annoying?! I love Swift and its compiler is amazing. But why does it have to do this? Anyway, the solution is easy enough. Your first option is to only initialize the struct internally inside of your package. This will allow you to take advantage of the free init provided by the compiler. If this isn't an option for you then you'll have to implement the init
method yourself.
public struct MyStruct {
public var myStringVar: String
public var myIntVar: Int
public init(myStringVar: String, myIntVar: Int) {
self.myStringVar = myStringVar
self.myIntVar = myIntVar
}
This is a small and easy example, but what if you have a lot of properties inside your struct? Simple, let Xcode generate the init code for you. To do this, "right click" on your struct's name to bring up a menu of options. Select Refactor
and then Generate Memberwise Initializer
.
This will generate the following code,
public struct MyStruct {
internal init(myStringVar: String, myIntVar: Int) {
self.myStringVar = myStringVar
self.myIntVar = myIntVar
}
public var myStringVar: String
public var myIntVar: Int
}
First thing you will need to do is change the Access control level. To do this delete the internal
access level and change it to public
. Now the compiler warning will be gone and you can move on.
If you want to keep the "free" init provided by the compiler while also exposing a public
initializer you can accomplish this by throwing the public init inside of an extension of your struct.
public struct MyStruct {
public var myStringVar: String
public var myIntVar: Int
}
extension MyStruct {
public init(myStringVar: String, myIntVar: Int) {
self.myStringVar = myStringVar
self.myIntVar = myIntVar
}
}