Whilst working through an edit person view in a SwiftUI project, I experienced the following two errors:
- Return from initializer without initializing all stored properties
- Cannot convert value of type 'String' to expected argument type 'Binding<String?>'
Many of the examples I was finding through Google were about showing one variable solutions. However, if you are like me - my situation didn't fall in to this category. As I have indicated, the view I was working on was a person view, so it had an assortment of data to gather... including:
- First name;
- Last name;
- Mobile; and
The list goes on.
What does my view look like:
struct EditRival: View { // MARK: - Propertiers @State private var typing = false @Environment(\.presentationMode) var presentationMode @EnvironmentObject var realmPerson: RealmPersons @Binding var uid: String @State private var firstName: String @State private var lastName: String @State private var nickName: String @State private var email: String @State private var phone: String @State private var imagePath: String @State private var langcode = "en" @State private var timezone = "" @State private var imageTakePicture = false @State private var showingImagePicker = false @State private var uiImage: UIImage? @State private var image: Image? @State private var showingErrorAlert = false @State private var errorAlertMessage = "" // .... more code .... // }
To call the edit view, I need to add an initialiser. In Swift, an initializer is a special init() function that we use to create objects of a particular class, struct or type. They’re used to construct (hence how come they are sometimes called constructors) an instance of the given type.
The initialiser is called using
init(rival: Rival)
Where Rival is a model.
struct Rival: Codable, Hashable, Identifiable { let id: Int var firstName: String var lastName: String var workEmail: String let phone: String var imageName: String var isFavorite: Bool var name: String { return [firstName, lastName].filter{ $0.count > 0}.joined(separator: " ") } }
So I need to use Rival struct to initialise the states in the editRival view. Something that is quite quick to set up.
_firstName = .init(initialValue: rival.firstName)
You will notice that the variable firstName when initialised the syntax has an underscore. _firstName will only be used in the initialiser. Subsequently, adding the other properties is a relatively set task.
init(rival: Rival) { _firstName = .init(initialValue: rival.firstName) _lastName = State(initialValue: rival.lastName) _nickName = State(initialValue: rival.firstName) _email = State(initialValue: rival.workEmail) }
In this instance not all of the properties have been initialised. Xcode will let you know with the following error message:
Return from initializer without initializing all stored properties
So you will need to go through and initialise all properties that are listed above, except obviously for those that have already been initialised. Such as imageTakePicture = false
init(rival: Rival) { _uid = .init(initialValue: rival.id) _firstName = State(initialValue: rival.firstName) _lastName = State(initialValue: rival.lastName) _nickName = State(initialValue: rival.firstName) _email = State(initialValue: rival.workEmail) _phone = State(initialValue: rival.phone) _imagePath = State(initialValue: rival.imageName) }
expected argument type 'Binding<String?>'
Now the _uid initialisation is showing an error
Cannot convert value of type 'String' to expected argument type 'Binding<String?>'
As their is a Binding statement, it's initialisation needs to be Binding.constant().
_uid = Binding.constant(String(rival.id))
The binding error has been solved and the initialisation becomes
init(rival: Rival) { _uid = Binding.constant(String(rival.id)) _firstName = .init(initialValue: rival.firstName) _lastName = State(initialValue: rival.lastName) _nickName = State(initialValue: rival.firstName) _email = State(initialValue: rival.workEmail) _phone = State(initialValue: rival.phone) _imagePath = State(initialValue: rival.imageName) }
The init function is complete. The Return from initializer without initializing all stored properties error is solved.
The code with the two errors has been solved. The updated editRival view looks like
struct EditRival: View { // MARK: - Propertiers @State private var typing = false @Environment(\.presentationMode) var presentationMode @EnvironmentObject var realmPerson: RealmPersons @Binding var uid: String @State private var firstName: String @State private var lastName: String @State private var nickName: String @State private var email: String @State private var phone: String @State private var imagePath: String @State private var langcode = "en" @State private var timezone = "" @State private var imageTakePicture = false @State private var showingImagePicker = false @State private var uiImage: UIImage? @State private var image: Image? @State private var showingErrorAlert = false @State private var errorAlertMessage = "" init(rival: Rival) { _uid = Binding.constant(String(rival.id)) _firstName = .init(initialValue: rival.firstName) _lastName = State(initialValue: rival.lastName) _nickName = State(initialValue: rival.firstName) _email = State(initialValue: rival.workEmail) _phone = State(initialValue: rival.phone) _imagePath = State(initialValue: rival.imageName) } // .... more code .... // }
Can you create this so that it is more developer friendly. However, in this instance I wanted to show the progress in the EditRival structure.