I want to decouple some views. For this case I created an initializer for a view with a custom argument in an extension in a second file. Unfortunately I get this message: "'self' used before all stored properties are initialized".
If I put view and extension in the same file, it works. But in this case, the view file needs to know the custom argument. This may not be needed in another project.
For simplicity, I have created a sample code that shows the problem. How can I initialize the @State property name in the extension initializer if the extension is in another file?
// ListCell.swift
import SwiftUI
struct ListCell: View {
@State var name: String
var count: Int
var body: some View {
HStack {
Text(name)
Text(": \(count)")
}
}
}
// ListCell+Ext.swift
import SwiftUI
extension ListCell {
init(name: String) {
self.name = name
count = 0
}
}
The problem is that @State var name: String
is actually sugar for a private value _name
with some extra logic. You are trying to set String
to the value that is exposed from the state (which is State<String>
). If you will try to set _name
in the extension the compiler will complain that you can't access this private value from the extension.
Seems like you can create a convenience init in your extension, while keeping the default one, and just using it:
// ListCell.swift
struct ListCell: View {
@State var name: String
var count: Int
var body: some View {
HStack {
Text(name)
Text(": \(count)")
}
}
}
// ListCell+Ext.swift
import SwiftUI
extension ListCell {
init(name: String) {
self.init(name: name, count: 0)
}
}
Note that Swift docs do say that a default struct initializer is only generated when there isn't a custom one ("Structure types automatically receive a memberwise initializer if they don’t define any of their own custom initializers" - under Memberwise Initializers) but seems like when using an extension like in your case the default init is still generated, and you can use it instead of creating one...
By the way, in general, @State
should be used when your view owns and creates the value, if it is passed from above, it's likely that you should actually use @Binding
instead.